Changeset 487 for trunk/lib/Stats.cc


Ignore:
Timestamp:
Oct 14, 2007, 12:20:17 AM (15 years ago)
Author:
Peter Johansson
Message:

fixes #272 in trunk and also #211 - split stats class into inherited and base class

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/lib/Stats.cc

    r483 r487  
    5151
    5252  Stats::Stats(const std::string& path)
     53    : valid_(true)
    5354  {
    5455    // Make sure latest revision is set properly
     
    5960
    6061
    61   std::vector<u_int> Stats::accumulated(const Map_& map) const
    62   {
    63     // sum of all users
    64     std::vector<u_int> sum(revision_+1);
    65     sum=std::accumulate(map.begin(), map.end(), sum,
    66                         PairValuePlus<std::string,u_int>());
    67 
    68     // calculate accumulated sum
    69     std::vector<u_int> accum(sum.size());
    70     std::partial_sum(sum.begin(),sum.end(),accum.begin());
    71     assert(sum.size()==accum.size());
    72     return accum;
    73   }
    74 
    75   std::vector<u_int> Stats::accumulated(const Map_& map,
    76                                         const std::string& user) const
    77   {
    78     if (!map.count(user))
    79       return std::vector<u_int>(last_changed_rev_,0);
    80     std::vector<u_int> vec=(map.find(user))->second;
    81 
    82     // static_cast to remove annoying compiler warning
    83     if (vec.size() < static_cast<size_t>(revision_+1))
    84       vec.insert(vec.end(), revision_+1-vec.size(), 0);
    85 
    86     std::vector<u_int> accum(vec.size());
    87     std::partial_sum(vec.begin(),vec.end(),accum.begin());
    88     return accum;
    89   }
    90 
    91   void Stats::add(const std::string& user, const u_int& rev,
    92                   const Parser::line_type& lt)
    93   {
    94     assert(user.size());
    95     authors_.insert(user);
    96 
    97     std::vector<u_int>* total = &(total_[user]);
    98     assert(total);
    99     if (total->size() < rev+1){
    100       total->reserve(revision_ + 1);
    101       total->insert(total->end(), rev - total->size(), 0);
    102       total->push_back(1);
     62  Stats::~Stats(void)
     63  {
     64  }
     65
     66
     67  void Stats::base_add(v_map::const_iterator first1,
     68                       v_map::const_iterator last1, v_map& map)
     69  {
     70    v_map::iterator first2(map.begin());
     71    v_map::key_compare compare;
     72    while ( first1 != last1) {
     73      // key of first1 less than key of first2
     74      if (first2==map.end() || compare(first1->first,first2->first)) {
     75        first2 = map.insert(first2, *first1);
     76        ++first1;
     77      }
     78      // key of first2 less than key of first1
     79      else if ( compare(first2->first, first1->first)) {
     80        ++first2;
     81      }
     82      // keys are equivalent
     83      else {
     84        VectorPlus<v_map::mapped_type::value_type> vp;
     85        first2->second = vp(first1->second, first2->second);
     86        ++first1;
     87        ++first2;
     88      }
    10389    }
    104     else
    105       ++(*total)[rev];
    106 
    107     std::vector<u_int>* code = &(code_[user]);
    108     assert(code);
    109     if (code->size() < rev+1){
    110       code->reserve(revision_ + 1);
    111       code->insert(code->end(), rev - code->size(), 0);
    112       if (lt == Parser::code)
    113         code->push_back(1);
    114       else
    115         code->push_back(0);
    116     }
    117     else if (lt == Parser::code)
    118       ++(*code)[rev];
    119 
    120     std::vector<u_int>* comments = &(comments_[user]);
    121     assert(comments);
    122     if (comments->size() < rev+1){
    123       comments->reserve(revision_ + 1);
    124       comments->insert(comments->end(), rev - comments->size(), 0);
    125       if (lt == Parser::comment)
    126         comments->push_back(1);
    127       else
    128         comments->push_back(0);
    129     }
    130     else if (lt == Parser::comment)
    131       ++(*comments)[rev];
    132 
    133     std::vector<u_int>* empty = &(empty_[user]);
    134     assert(empty);
    135     if (empty->size() < rev+1){
    136       empty->reserve(revision_ + 1);
    137       empty->insert(empty->end(), rev - empty->size(), 0);
    138       if (lt == Parser::empty)
    139         empty->push_back(1);
    140       else
    141         empty->push_back(0);
    142     }
    143     else if (lt == Parser::empty)
    144       ++(*empty)[rev];
    145   }
    146 
     90  }
     91
     92
     93  void Stats::add_author(std::string name)
     94  {
     95    authors_.insert(name);
     96  }
     97
     98
     99  void Stats::add_authors(std::set<std::string>::const_iterator first,
     100                          std::set<std::string>::const_iterator last)
     101  {
     102    authors_.insert(first, last);
     103  }
     104
     105
     106  const std::set<std::string>& Stats::authors(void) const
     107  {
     108    return authors_;
     109  }
     110
     111
     112  void Stats::base_add(const Stats& rhs)
     113  {
     114    revision_ = std::max(revision_, rhs.revision_);
     115    last_changed_rev_ = std::max(last_changed_rev_, rhs.last_changed_rev_);
     116    add_authors(rhs.authors().begin(), rhs.authors().end());
     117    valid_ &= rhs.valid();
     118    // if cache not valid no need to update it
     119    if (valid()) {
     120      base_add(rhs.code_.begin(), rhs.code_.end(), code_);
     121      base_add(rhs.comments_.begin(), rhs.comments_.end(), comments_);
     122      base_add(rhs.other_.begin(), rhs.other_.end(), other_);
     123      base_add(rhs.total_.begin(), rhs.total_.end(), total_);
     124    }
     125  }
     126
     127
     128  u_int Stats::code(const std::string& user) const
     129  {
     130    return get_vector(code_, "all").back();
     131  }
     132
     133
     134  u_int Stats::comments(const std::string& user) const
     135  {
     136    return get_vector(comments_, "all").back();
     137  }
     138
     139
     140  u_int Stats::empty(const std::string& user) const
     141  {
     142    return get_vector(other_, "all").back();
     143  }
     144
     145
     146  const std::vector<u_int>& Stats::get_vector(const v_map& m,
     147                                              std::string user) const
     148  {
     149    // Peter, we should avoid calling this function prior all
     150    // statistics are calculated. If, for some reason, this is not
     151    // possible call update_stats() first, however, that function is
     152    // not const so some redesign is needed (e.g. making cache mutable
     153    // or making them pointers)
     154    assert(valid() && "trying to access invalid cache of statistics");
     155    v_map::const_iterator iter(m.find(std::string(user)));
     156    if (iter==m.end())
     157      throw std::runtime_error(user+std::string(" not found i Stats"));
     158    return iter->second;
     159  }
    147160
    148161  bool Stats::load_cache(std::istream& is)
    149162  {
     163
    150164    svn_revnum_t rev;
    151165    is >> rev;
     
    163177      authors_.insert(str);
    164178    }
    165     getline(is, str);
    166     if (str!=code_cache()){
    167       return false;
    168     }
    169     load(is, code_);
    170     getline(is, str);
    171     getline(is, str);
    172     if (str!=comments_cache()){
    173       return false;
    174     }
    175     load(is, comments_);
    176     getline(is, str);
    177     getline(is, str);
    178     if (str!=empty_cache()){
    179       return false;
    180     }
    181     load(is, empty_);
    182     getline(is, str);
    183     getline(is, str);
    184     if (str!=total_cache()){
    185       return false;
    186     }
    187     load(is, total_);
    188     getline(is,str);
    189     getline(is,str);
    190     return str==end_of_cache();
    191   }
    192 
    193 
    194   void Stats::load(std::istream& is, Map_& m)
    195   {
    196     m.clear();
    197     while (m.size() < authors_.size() && is.good()) {
    198       std::string name;
    199       std::getline(is, name);
    200       assert(name.size());
    201       std::vector<u_int>& vec=m[name];
    202       size_t revs=0;
    203       is >> revs;
    204       vec.reserve(revs);
    205       while (vec.size() < revs) {
    206         u_int tmp;
    207         is >> tmp;
    208         vec.push_back(tmp);
    209       }
    210     }
     179    return do_load_cache(is);
     180  }
     181
     182
     183  svn_revnum_t Stats::last_changed_rev(void) const
     184  {
     185    return last_changed_rev_;
     186  }
     187
     188
     189  u_int Stats::lines(const std::string& user) const
     190  {
     191    assert(valid()); return get_vector(total_, "all").back();
    211192  }
    212193
     
    214195  void Stats::parse(const std::string& path)
    215196  {
    216     Parser parser(path);
    217     std::vector<Parser::line_type>::const_iterator count=parser.type().begin();
    218 
    219     SVNblame svn_blame(path);
    220     while (svn_blame.valid()) {
    221       add(svn_blame.author(), svn_blame.revision(), *count);
    222       svn_blame.next_line();
    223       ++count;
    224     }
    225    
    226   }
    227 
     197    valid_=false;
     198    // First we let inherited class do parsing
     199    do_parse(path);
     200    // then we fill up with statistics
     201
     202    update_stats();
     203  }
    228204
    229205  std::string Stats::plot(const std::string& filename,
    230206                          const std::string& linetype) const
    231207  {
    232     plot_init(filename);
    233     GnuplotFE* gp=GnuplotFE::instance();
    234     const Map_* stat=NULL;
    235     if (linetype=="total")
    236       stat = &total_;
    237     else if (linetype=="code")
    238       stat = &code_;
    239     else if (linetype=="comments")
    240       stat = &comments_;
    241     else if (linetype=="empty")
    242       stat = &empty_;
    243     assert(stat);
    244     std::vector<u_int> total=accumulated(*stat);   
    245     double yrange_max=1.03*total.back()+1;
    246     gp->yrange(yrange_max);
    247 
    248     typedef std::vector<std::pair<std::string, std::vector<u_int> > > vec_type;
    249     vec_type author_cont;
    250     author_cont.reserve(stat->size());
    251     for (MapConstIter_ i= stat->begin(); i != stat->end(); ++i) {
    252       author_cont.push_back(std::make_pair(i->first,
    253                                            accumulated(*stat,i->first)));
    254     }
    255 
    256     LessReversed<std::vector<u_int> > lr;
    257     PairSecondCompare<std::string, std::vector<u_int>,
    258       LessReversed<std::vector<u_int> > > compare(lr);
    259     std::sort(author_cont.begin(), author_cont.end(), compare);
    260 
    261     size_t plotno=author_cont.size();
    262     std::stringstream ss;
    263     vec_type::iterator end(author_cont.end());
    264     for (vec_type::iterator i(author_cont.begin()); i!=end; ++i) {
    265       ss.str("");
    266       ss << "set key height " << 2*plotno;
    267       gp->command(ss.str());
    268       ss.str("");
    269       ss << i->second.back() << " " << i->first;
    270       gp->yrange(yrange_max);
    271       gp->linetitle(ss.str());
    272       ss.str("");
    273       ss << "steps " << --plotno+2;
    274       gp->linestyle(ss.str());
    275       gp->plot(i->second);
    276     }
    277     ss.str("");
    278     ss << total.back() << " total";
    279     gp->command("set key height 0");
    280     gp->linetitle(ss.str());
    281     gp->linestyle("steps 1");
    282     gp->plot(total);
    283 
    284     gp->command("unset multiplot");
    285     gp->yrange();
    286 
    287     return filename;
     208    return do_plot(filename, linetype);
    288209  }
    289210
     
    304225  void Stats::plot_summary(const std::string& filename) const
    305226  {
     227    assert(valid());
    306228    plot_init(filename);
    307229    GnuplotFE* gp=GnuplotFE::instance();
    308     std::vector<u_int> total=accumulated(total_);   
     230    std::vector<u_int> total = get_vector(total_, "all");
    309231    double yrange_max=1.03*total.back()+1;
    310232    gp->yrange(yrange_max);
     
    312234   
    313235    ss.str("");
    314     std::vector<u_int> x=accumulated(code_);   
     236    std::vector<u_int> x(get_vector(code_, "all"));
    315237    ss << x.back() << " code";
    316238    gp->command("set key height 2");
     
    320242
    321243    ss.str("");
    322     x=accumulated(comments_);   
     244    x = get_vector(code_, "all");
    323245    ss << x.back() << " comment";
    324246    gp->command("set key height 4");
     
    328250
    329251    ss.str("");
    330     x=accumulated(empty_);   
     252    x = get_vector(code_, "all");
    331253    ss << x.back() << " other";
    332254    gp->command("set key height 6");
     
    354276    std::copy(authors_.begin(), authors_.end(),
    355277              std::ostream_iterator<std::string>(os, "\n"));
    356     os << code_cache() << "\n";
    357     print(os, code_);
    358     os << "\n" << comments_cache() << "\n";
    359     print(os, comments_);
    360     os << "\n" << empty_cache() << "\n";
    361     print(os, empty_);
    362     os << "\n" << total_cache() << "\n";
    363     print(os, total_);
    364     os << "\n" << end_of_cache() << "\n";
    365   }
    366 
    367 
    368   void Stats::print(std::ostream& os, const Map_& m) const
    369   {
    370     for (MapConstIter_ i(m.begin()); i!=m.end(); ++i){
    371       os << i->first << "\n";
    372       os << i->second.size() << " ";
    373       std::copy(i->second.begin(), i->second.end(),
    374                 std::ostream_iterator<u_int>(os, " "));
     278    do_print(os);
     279  }
     280
     281
     282  void Stats::reset(void)
     283  {
     284    do_reset();
     285  }
     286
     287
     288  void Stats::update_stats(void)
     289  {
     290    for (std::set<std::string>::const_iterator iter(authors_.begin());
     291         iter!=authors_.end(); ++iter) {
     292      std::vector<u_int> code(vector("code", *iter));
     293      code_[*iter] = code;
     294      std::vector<u_int> comments(vector("comments", *iter));
     295      comments_[*iter] = comments;
     296      std::vector<u_int> other(vector("other", *iter));
     297      other_[*iter] = other;
     298      VectorPlus<u_int> vp;
     299      total_[*iter] = vp(vp(code, comments),other);
    375300    }
    376   }
    377 
    378 
    379   Stats& Stats::operator+=(const Stats& other)
    380   {
    381     for (MapConstIter_ o_i= other.code_.begin();
    382          o_i != other.code_.end(); ++o_i)
    383     {
    384       std::pair<MapIter_,bool> result = code_.insert(*o_i);
    385       if (!result.second)
    386         code_[(*(result.first)).first] =
    387           VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
    388  
    389     }
    390  
    391     for (MapConstIter_ o_i= other.comments_.begin();
    392          o_i != other.comments_.end(); ++o_i)
    393     {
    394       std::pair<MapIter_,bool> result = comments_.insert(*o_i);
    395       if (!result.second)
    396         comments_[(*(result.first)).first] =
    397           VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
    398  
    399     }
    400    
    401     for (MapConstIter_ o_i= other.empty_.begin();
    402          o_i != other.empty_.end(); ++o_i)
    403     {
    404       std::pair<MapIter_,bool> result = empty_.insert(*o_i);
    405       if (!result.second)
    406         empty_[(*(result.first)).first] =
    407           VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
    408  
    409     }
    410    
    411     for (MapConstIter_ o_i= other.total_.begin();
    412          o_i != other.total_.end(); ++o_i)
    413     {
    414       std::pair<MapIter_,bool> result = total_.insert(*o_i);
    415       if (!result.second)
    416         total_[(*(result.first)).first] =
    417           VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
    418  
    419     }
    420    
    421     if (!other.authors().empty())
    422       authors_.insert(other.authors().begin(), other.authors().end());
    423     return *this;
    424   }
     301    std::vector<u_int> init(revision()+1);
     302    code_["all"]=std::accumulate(code_.begin(), code_.end(), init,
     303                                 PairValuePlus<std::string,u_int>());
     304    comments_["all"]=std::accumulate(comments_.begin(), comments_.end(), init,
     305                                     PairValuePlus<std::string,u_int>());
     306    other_["all"]=std::accumulate(other_.begin(), other_.end(), init,
     307                                  PairValuePlus<std::string,u_int>());
     308    VectorPlus<u_int> vp;
     309    total_["all"] = vp(vp(code_["all"], comments_["all"]), other_["all"]);
     310    valid_=true;
     311  }
     312
    425313
    426314}} // end of namespace svndigest and namespace theplu
Note: See TracChangeset for help on using the changeset viewer.