source: trunk/lib/Stats.cc @ 483

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

fixed bug in parsing of cache and removed a debug output

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.5 KB
Line 
1// $Id: Stats.cc 483 2007-10-13 02:41:06Z peter $
2
3/*
4  Copyright (C) 2005 Peter Johansson
5  Copyright (C) 2006, 2007 Jari Häkkinen, Peter Johansson
6
7  This file is part of svndigest, http://trac.thep.lu.se/trac/svndigest
8
9  svndigest is free software; you can redistribute it and/or modify it
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
14  svndigest is distributed in the hope that it will be useful, but
15  WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
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
25#include "Stats.h"
26
27#include "Functor.h"
28#include "GnuplotFE.h"
29#include "SVNblame.h"
30#include "SVNinfo.h"
31#include "utility.h"
32
33#include <algorithm>
34#include <cassert>
35#include <cstdlib>
36#include <fstream>
37#include <iostream>
38#include <iterator>
39#include <map>
40#include <numeric>
41#include <string>
42#include <sstream>
43#include <unistd.h>
44#include <utility>
45#include <vector>
46
47
48namespace theplu{
49namespace svndigest{
50
51
52  Stats::Stats(const std::string& path)
53  {
54    // Make sure latest revision is set properly
55    SVNinfo svn_info(path);
56    revision_=svn_info.rev();
57    last_changed_rev_=svn_info.last_changed_rev();
58  }
59
60
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);
103    }
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
147
148  bool Stats::load_cache(std::istream& is)
149  {
150    svn_revnum_t rev;
151    is >> rev;
152    if (rev<last_changed_rev_){
153      return false; // cache is not up to date
154    }
155    size_t a_size=0;
156    authors_.clear();
157    is >> a_size;
158    std::string str;
159    getline(is, str);
160    while (authors_.size()<a_size){
161      getline(is, str);
162      assert(str.size());
163      authors_.insert(str);
164    }
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    }
211  }
212
213
214  void Stats::parse(const std::string& path)
215  {
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
228
229  std::string Stats::plot(const std::string& filename,
230                          const std::string& linetype) const
231  {
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;
288  }
289
290
291  void Stats::plot_init(const std::string& filename) const
292  {
293    GnuplotFE* gp=GnuplotFE::instance();
294    gp->command("set term png");
295    gp->command("set output '"+filename+"'");
296    gp->command("set xtics nomirror");
297    gp->command("set ytics nomirror");
298    gp->command("set key default");
299    gp->command("set key left Left reverse");
300    gp->command("set multiplot");
301  }
302
303
304  void Stats::plot_summary(const std::string& filename) const
305  {
306    plot_init(filename);
307    GnuplotFE* gp=GnuplotFE::instance();
308    std::vector<u_int> total=accumulated(total_);   
309    double yrange_max=1.03*total.back()+1;
310    gp->yrange(yrange_max);
311    std::stringstream ss;
312   
313    ss.str("");
314    std::vector<u_int> x=accumulated(code_);   
315    ss << x.back() << " code";
316    gp->command("set key height 2");
317    gp->linetitle(ss.str());
318    gp->linestyle("steps 2");
319    gp->plot(x);
320
321    ss.str("");
322    x=accumulated(comments_);   
323    ss << x.back() << " comment";
324    gp->command("set key height 4");
325    gp->linetitle(ss.str());
326    gp->linestyle("steps 3");
327    gp->plot(x);
328
329    ss.str("");
330    x=accumulated(empty_);   
331    ss << x.back() << " other";
332    gp->command("set key height 6");
333    gp->linetitle(ss.str());
334    gp->linestyle("steps 4");
335    gp->plot(x);
336
337    ss.str("");
338    ss << total.back() << " total";
339    gp->command("set key height 0");
340    gp->linetitle(ss.str());
341    gp->linestyle("steps 1");
342    gp->plot(total);
343
344    gp->command("unset multiplot");
345    gp->yrange();
346  }
347
348
349  void Stats::print(std::ostream& os) const
350  {
351    os << last_changed_rev_ << " ";
352    os << authors_.size() << "\n";
353
354    std::copy(authors_.begin(), authors_.end(), 
355              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, " "));
375    }
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  }
425
426}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.