source: trunk/lib/Stats.cc @ 439

Last change on this file since 439 was 439, checked in by Peter Johansson, 14 years ago

Merged patch release 0.6.1 to the trunk. Delta 0.6.1 - 0.6

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.5 KB
RevLine 
[84]1// $Id: Stats.cc 439 2007-07-09 21:04:52Z peter $
[14]2
[84]3/*
4  Copyright (C) 2005 Peter Johansson
[381]5  Copyright (C) 2006, 2007 Jari Häkkinen, Peter Johansson
[84]6
[439]7  This file is part of svndigest, http://trac.thep.lu.se/trac/svndigest
[84]8
[149]9  svndigest is free software; you can redistribute it and/or modify it
[84]10  under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13
[149]14  svndigest is distributed in the hope that it will be useful, but
[84]15  WITHOUT ANY WARRANTY; without even the implied warranty of
[149]16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
[84]17  General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22  02111-1307, USA.
23*/
24
[14]25#include "Stats.h"
[129]26
[73]27#include "GnuplotFE.h"
[138]28#include "SVNblame.h"
[129]29#include "SVNinfo.h"
[14]30#include "utility.h"
31
[41]32#include <algorithm>
[65]33#include <cassert>
[63]34#include <cstdlib>
[60]35#include <iostream>
[14]36#include <map>
37#include <numeric>
38#include <string>
[138]39#include <sstream>
[44]40#include <unistd.h>
[23]41#include <utility>
[14]42#include <vector>
43
[63]44
[14]45namespace theplu{
[149]46namespace svndigest{
[14]47
48
[80]49  Stats::Stats(const std::string& path)
50  {
51    // Make sure latest revision is set properly
[129]52    SVNinfo svn_info(path);
53    revision_=svn_info.rev();
54    last_changed_rev_=svn_info.last_changed_rev();
[80]55  }
56
57
[118]58  std::vector<u_int> Stats::accumulated(const Map_& map) const
[14]59  {
[36]60    // sum of all users
[118]61    std::vector<u_int> sum(revision_+1);
62    sum=std::accumulate(map.begin(), map.end(), sum,
[36]63                        PairValuePlus<std::string,u_int>());
[14]64
65    // calculate accumulated sum
[74]66    std::vector<u_int> accum(sum.size());
[60]67    std::partial_sum(sum.begin(),sum.end(),accum.begin());
[63]68    assert(sum.size()==accum.size());
[14]69    return accum;
70  }
71
[118]72  std::vector<u_int> Stats::accumulated(const Map_& map, 
73                                        const std::string& user) const
[14]74  {
[118]75    if (!map.count(user))
[213]76      return std::vector<u_int>(last_changed_rev_,0);
[118]77    std::vector<u_int> vec=(map.find(user))->second;
[131]78
79    // static_cast to remove annoying compiler warning
80    if (vec.size() < static_cast<size_t>(revision_+1))
[112]81      vec.insert(vec.end(), revision_+1-vec.size(), 0);
[35]82
[74]83    std::vector<u_int> accum(vec.size());
[14]84    std::partial_sum(vec.begin(),vec.end(),accum.begin());
85    return accum;
86  }
87
[118]88  void Stats::add(const std::string& user, const u_int& rev, 
[138]89                  const Parser::line_type& lt)
[14]90  {
[118]91    authors_.insert(user);
92
93    std::vector<u_int>* total = &(total_[user]);
[265]94    assert(total);
[118]95    if (total->size() < rev+1){
96      total->reserve(revision_ + 1);
97      total->insert(total->end(), rev - total->size(), 0);
98      total->push_back(1);
[14]99    }
100    else
[173]101      ++(*total)[rev];
[118]102
103    std::vector<u_int>* code = &(code_[user]);
[265]104    assert(code);
[118]105    if (code->size() < rev+1){
106      code->reserve(revision_ + 1);
107      code->insert(code->end(), rev - code->size(), 0);
108      if (lt == Parser::code)
109        code->push_back(1);
110      else 
111        code->push_back(0);
112    }
113    else if (lt == Parser::code)
[173]114      ++(*code)[rev];
[118]115
116    std::vector<u_int>* comments = &(comments_[user]);
[265]117    assert(comments);
[118]118    if (comments->size() < rev+1){
119      comments->reserve(revision_ + 1);
120      comments->insert(comments->end(), rev - comments->size(), 0);
121      if (lt == Parser::comment)
122        comments->push_back(1);
123      else 
124        comments->push_back(0);
125    }
126    else if (lt == Parser::comment)
[173]127      ++(*comments)[rev];
[202]128
129    std::vector<u_int>* empty = &(empty_[user]);
[265]130    assert(empty);
[202]131    if (empty->size() < rev+1){
132      empty->reserve(revision_ + 1);
133      empty->insert(empty->end(), rev - empty->size(), 0);
[208]134      if (lt == Parser::empty)
[202]135        empty->push_back(1);
136      else 
137        empty->push_back(0);
138    }
[208]139    else if (lt == Parser::empty)
[202]140      ++(*empty)[rev];
[14]141  }
142
[60]143
[185]144  void Stats::parse(const std::string& path)
[60]145  {
[118]146    Parser parser(path);
147    std::vector<Parser::line_type>::const_iterator count=parser.type().begin();
148
[185]149    SVNblame svn_blame(path);
[381]150    while (svn_blame.valid()) {
151      add(svn_blame.author(), svn_blame.revision(), *count);
152      svn_blame.next_line();
[361]153      ++count;
[138]154    }
[202]155   
[60]156  }
157
158
[345]159  std::string Stats::plot(const std::string& filename,
160                          const std::string& linetype) const
161  {
162    plot_init(filename);
163    GnuplotFE* gp=GnuplotFE::instance();
[209]164    const Map_* stat=NULL;
165    if (linetype=="total")
166      stat = &total_;
167    else if (linetype=="code")
168      stat = &code_;
169    else if (linetype=="comments")
170      stat = &comments_;
171    else if (linetype=="empty")
172      stat = &empty_;
173    assert(stat);
174    std::vector<u_int> total=accumulated(*stat);   
[76]175    double yrange_max=1.03*total.back()+1;
[74]176    gp->yrange(yrange_max);
[371]177
178    typedef std::vector<std::pair<std::string, std::vector<u_int> > > vec_type;
179    vec_type author_cont;
180    author_cont.reserve(stat->size());
181    for (MapConstIter_ i= stat->begin(); i != stat->end(); ++i) {
182      author_cont.push_back(std::make_pair(i->first,
183                                           accumulated(*stat,i->first)));
184    }
185
186    LessReversed<std::vector<u_int> > lr;
187    PairSecondCompare<std::string, std::vector<u_int>, 
188      LessReversed<std::vector<u_int> > > compare(lr);
189    std::sort(author_cont.begin(), author_cont.end(), compare);
190
191    size_t plotno=author_cont.size();
[77]192    std::stringstream ss;
[371]193    vec_type::iterator end(author_cont.end());
194    for (vec_type::iterator i(author_cont.begin()); i!=end; ++i) {
[77]195      ss.str("");
196      ss << "set key height " << 2*plotno;
197      gp->command(ss.str());
198      ss.str("");
[371]199      ss << i->second.back() << " " << i->first;
[74]200      gp->yrange(yrange_max);
[77]201      gp->linetitle(ss.str());
202      ss.str("");
[371]203      ss << "steps " << --plotno+2;
[77]204      gp->linestyle(ss.str());
[382]205      gp->plot(i->second);
[39]206    }
[77]207    ss.str("");
208    ss << total.back() << " total";
[76]209    gp->command("set key height 0");
[77]210    gp->linetitle(ss.str());
[76]211    gp->linestyle("steps 1");
212    gp->plot(total);
213
[73]214    gp->command("unset multiplot");
[74]215    gp->yrange();
[36]216
[101]217    return filename;
[34]218  }
219
[118]220
[382]221  void Stats::plot_init(const std::string& filename) const
222  {
223    GnuplotFE* gp=GnuplotFE::instance();
224    gp->command("set term png");
225    gp->command("set output '"+filename+"'");
226    gp->command("set xtics nomirror");
227    gp->command("set ytics nomirror");
228    gp->command("set key default");
229    gp->command("set key left Left reverse");
230    gp->command("set multiplot");
231  }
232
233
[345]234  void Stats::plot_summary(const std::string& filename) const
235  {
236    plot_init(filename);
237    GnuplotFE* gp=GnuplotFE::instance();
238    std::vector<u_int> total=accumulated(total_);   
239    double yrange_max=1.03*total.back()+1;
240    gp->yrange(yrange_max);
241    std::stringstream ss;
242   
243    ss.str("");
244    std::vector<u_int> x=accumulated(code_);   
245    ss << x.back() << " code";
246    gp->command("set key height 2");
247    gp->linetitle(ss.str());
248    gp->linestyle("steps 2");
249    gp->plot(x);
250
251    ss.str("");
252    x=accumulated(comments_);   
253    ss << x.back() << " comment";
254    gp->command("set key height 4");
255    gp->linetitle(ss.str());
256    gp->linestyle("steps 3");
257    gp->plot(x);
258
259    ss.str("");
260    x=accumulated(empty_);   
261    ss << x.back() << " other";
262    gp->command("set key height 6");
263    gp->linetitle(ss.str());
264    gp->linestyle("steps 4");
265    gp->plot(x);
266
267    ss.str("");
268    ss << total.back() << " total";
269    gp->command("set key height 0");
270    gp->linetitle(ss.str());
271    gp->linestyle("steps 1");
272    gp->plot(total);
273
274    gp->command("unset multiplot");
275    gp->yrange();
276  }
277
278
[14]279  Stats& Stats::operator+=(const Stats& other)
280  {
[118]281    for (MapConstIter_ o_i= other.code_.begin(); 
282         o_i != other.code_.end(); ++o_i)
[14]283    {
[118]284      std::pair<MapIter_,bool> result = code_.insert(*o_i);
[14]285      if (!result.second)
[118]286        code_[(*(result.first)).first] = 
[23]287          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
[14]288 
289    }
[118]290 
291    for (MapConstIter_ o_i= other.comments_.begin(); 
292         o_i != other.comments_.end(); ++o_i)
293    {
294      std::pair<MapIter_,bool> result = comments_.insert(*o_i);
295      if (!result.second)
296        comments_[(*(result.first)).first] = 
297          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
298 
299    }
300   
[202]301    for (MapConstIter_ o_i= other.empty_.begin(); 
302         o_i != other.empty_.end(); ++o_i)
303    {
304      std::pair<MapIter_,bool> result = empty_.insert(*o_i);
305      if (!result.second)
306        empty_[(*(result.first)).first] = 
307          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
308 
309    }
310   
[118]311    for (MapConstIter_ o_i= other.total_.begin(); 
312         o_i != other.total_.end(); ++o_i)
313    {
314      std::pair<MapIter_,bool> result = total_.insert(*o_i);
315      if (!result.second)
316        total_[(*(result.first)).first] = 
317          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
318 
319    }
320   
321    if (!other.authors().empty())
322      authors_.insert(other.authors().begin(), other.authors().end());
[14]323    return *this;
324  }
325
[149]326}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.