source: trunk/lib/Stats.cc @ 532

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

Node holds a StatsCollection? rather than a Stats object.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.3 KB
Line 
1// $Id: Stats.cc 532 2007-12-26 01:47:03Z 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/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  Stats::~Stats(void)
62  {
63  }
64
65
66  void Stats::accumulate(std::vector<u_int>& vec) const
67  {
68    if (vec.empty()){
69      vec.resize(last_changed_rev(),0);
70      return;
71    }
72    std::partial_sum(vec.begin(),vec.end(),vec.begin());
73    // static_cast to remove annoying compiler warning
74    if (vec.size() < static_cast<size_t>(revision()+1))
75      vec.insert(vec.end(), revision()+1-vec.size(), vec.back());
76  }
77
78
79  void Stats::accumulate_stats(void)
80  {
81    for (std::set<std::string>::const_iterator iter(authors().begin());
82         iter!=authors().end(); ++iter) {
83      std::vector<u_int>& code = code_[*iter];
84      assert(code.size()<60);
85      accumulate(code);
86      std::vector<u_int>& comments = comments_[*iter];
87      assert(comments.size()<60);
88      accumulate(comments);
89      std::vector<u_int>& other = other_[*iter];
90      assert(other.size()<60);
91      accumulate(other);
92
93      VectorPlus<u_int> vp;
94      total_[*iter] = vp(vp(code, comments),other);
95    }
96    std::vector<u_int> init(revision()+1);
97    code_["all"]=std::accumulate(code_.begin(), code_.end(), init,
98                                 PairValuePlus<std::string,u_int>());
99    comments_["all"]=std::accumulate(comments_.begin(), comments_.end(), init,
100                                     PairValuePlus<std::string,u_int>());
101    other_["all"]=std::accumulate(other_.begin(), other_.end(), init,
102                                  PairValuePlus<std::string,u_int>());
103    VectorPlus<u_int> vp;
104    total_["all"] = vp(vp(code_["all"], comments_["all"]), other_["all"]);
105  }
106
107
108  void Stats::add(const std::string& user, const u_int& rev, 
109                  const Parser::line_type& lt, u_int n)
110  {
111    assert(user.size());
112    add_author(user);
113
114    std::vector<u_int>& total = total_[user];
115    if (total.size() < rev+1){
116      total.reserve(revision() + 1);
117      total.insert(total.end(), rev - total.size(), 0);
118      total.push_back(n);
119    }
120    else
121      total[rev]+=n;
122
123    std::vector<u_int>& code = code_[user];
124    if (code.size() < rev+1){
125      code.reserve(rev+1);
126      code.resize(rev);
127      if (lt == Parser::code)
128        code.push_back(n);
129      else 
130        code.push_back(0);
131    }
132    else if (lt == Parser::code)
133      code[rev]+=n;
134
135    std::vector<u_int>& comments = comments_[user];
136    if (comments.size() < rev+1){
137      comments.reserve(revision() + 1);
138      comments.insert(comments.end(), rev - comments.size(), 0);
139      if (lt == Parser::comment)
140        comments.push_back(n);
141      else 
142        comments.push_back(0);
143    }
144    else if (lt == Parser::comment)
145      comments[rev]+=n;
146
147    std::vector<u_int>& other = other_[user];
148    if (other.size() < rev+1){
149      other.reserve(revision() + 1);
150      other.insert(other.end(), rev - other.size(), 0);
151      if (lt == Parser::other)
152        other.push_back(n);
153      else 
154        other.push_back(0);
155    }
156    else if (lt == Parser::other)
157      other[rev]+=n;
158  }
159
160
161  void Stats::add_author(std::string name)
162  {
163    authors_.insert(name);
164  }
165
166
167  void Stats::add_authors(std::set<std::string>::const_iterator first, 
168                          std::set<std::string>::const_iterator last)
169  {
170    authors_.insert(first, last);
171  }
172
173
174  const std::set<std::string>& Stats::authors(void) const
175  {
176    return authors_;
177  }
178
179
180  u_int Stats::code(const std::string& user) const
181  {
182    return get_back(code_, user);
183  }
184
185
186  u_int Stats::comments(const std::string& user) const
187  {
188    return get_back(comments_, user);
189  }
190
191
192  u_int Stats::empty(const std::string& user) const
193  {
194    return get_back(other_, user);
195  }
196
197
198  u_int Stats::get_back(const Author2Vector& m, std::string user) const
199  {
200    A2VConstIter iter(m.find(std::string(user)));
201    if (iter==m.end() || iter->second.empty()) 
202      return 0;
203    return iter->second.back();
204  }
205
206
207  const std::vector<u_int>& Stats::get_vector(const Author2Vector& m, 
208                                              std::string user) const
209  {
210    A2VConstIter iter(m.find(std::string(user)));
211    if (iter==m.end()) 
212      throw std::runtime_error(user+std::string(" not found i Stats"));
213    return iter->second;
214  }
215
216
217  svn_revnum_t Stats::last_changed_rev(void) const
218  {
219    return last_changed_rev_;
220  }
221
222
223  u_int Stats::lines(const std::string& user) const
224  {
225    return get_back(total_, user);
226  }
227
228
229  void Stats::load(std::istream& is, Author2Vector& m)
230  {
231    while (m.size() < authors().size()+1 && is.good()) {
232      std::string name;
233      std::getline(is, name);
234      if (name.empty())
235        continue;
236      std::vector<u_int>& vec=m[name];
237      svn_revnum_t rev=0;
238      while (rev < last_changed_rev() && is.good()) {
239        u_int count;
240        is >> count;
241        if ( rev+1 > static_cast<svn_revnum_t>(vec.size()) )
242          vec.resize(rev+1);
243        vec[rev] = count;
244        is >> rev;
245      }
246    }
247  }
248
249
250  bool Stats::load_cache(std::istream& is)
251  {
252    svn_revnum_t rev;
253    is >> rev;
254    if (rev<last_changed_rev()){
255      return false; // cache is not up to date
256    }
257    reset();
258    size_t a_size=0;
259    is >> a_size;
260    std::string str;
261    while (authors().size()<a_size && is.good()){
262      getline(is, str);
263      if (str.size())
264        add_author(str);
265    }
266    getline(is, str);
267    if (str!=code_cache()){
268      return false;
269    }
270    load(is, code_);
271    getline(is, str);
272    getline(is, str);
273    if (str!=comments_cache()){
274      return false;
275    }
276    load(is, comments_);
277    getline(is, str);
278    getline(is, str);
279    if (str!=other_cache()){
280      return false;
281    }
282    load(is, other_);
283    getline(is, str);
284    getline(is, str);
285    if (str!=total_cache()){
286      return false;
287    }
288    load(is, total_);
289    getline(is,str);
290    getline(is,str);
291    if (total_.size()>static_cast<size_t>(rev+1))
292      return false;
293    accumulate_stats();
294    return false;
295    return str==end_of_cache();
296  }
297
298
299  void Stats::map_add(A2VConstIter first1, A2VConstIter last1, 
300                      Author2Vector& map)
301  {
302    A2VIter first2(map.begin());
303    Author2Vector::key_compare compare;
304    while ( first1 != last1) { 
305      // key of first1 less than key of first2
306      if (first2==map.end() || compare(first1->first,first2->first)) {
307        first2 = map.insert(first2, *first1);
308        ++first1;
309      }
310      // key of first2 less than key of first1
311      else if ( compare(first2->first, first1->first)) {
312        ++first2;
313      }
314      // keys are equivalent
315      else {
316        VectorPlus<Author2Vector::mapped_type::value_type> vp;
317        first2->second = vp(first1->second, first2->second);
318        ++first1;
319        ++first2;
320      }
321    }
322  }
323
324
325  void Stats::parse(const std::string& path)
326  {
327    do_parse(path);
328    accumulate_stats();
329  }
330
331  std::string Stats::plot(const std::string& filename,
332                          const std::string& linetype) const
333  {
334    plot_init(filename);
335    GnuplotFE* gp=GnuplotFE::instance();
336    const Author2Vector* stat=NULL;
337    if (linetype=="total")
338      stat = &total_;
339    else if (linetype=="code")
340      stat = &code_;
341    else if (linetype=="comments")
342      stat = &comments_;
343    else if (linetype=="empty")
344      stat = &other_;
345    assert(stat);
346    assert(stat->size());
347    std::vector<u_int> total=get_vector(*stat, "all");   
348    double yrange_max=1.03*total.back()+1;
349    gp->yrange(yrange_max);
350
351    typedef std::vector<std::pair<std::string, std::vector<u_int> > > vec_type;
352    vec_type author_cont;
353    author_cont.reserve(stat->size());
354    for (std::set<std::string>::const_iterator i=authors_.begin(); 
355         i != authors_.end(); ++i) {
356      if (lines(*i))
357        author_cont.push_back(std::make_pair(*i,get_vector(*stat,*i)));
358    }
359
360    LessReversed<std::vector<u_int> > lr;
361    PairSecondCompare<std::string, std::vector<u_int>, 
362      LessReversed<std::vector<u_int> > > compare(lr);
363    std::sort(author_cont.begin(), author_cont.end(), compare);
364
365    size_t plotno=author_cont.size();
366    std::stringstream ss;
367    vec_type::iterator end(author_cont.end());
368    for (vec_type::iterator i(author_cont.begin()); i!=end; ++i) {
369      ss.str("");
370      ss << "set key height " << 2*plotno;
371      gp->command(ss.str());
372      ss.str("");
373      ss << get_back(*stat, i->first) << " " << i->first;
374      gp->yrange(yrange_max);
375      gp->linetitle(ss.str());
376      ss.str("");
377      ss << "steps " << --plotno+2;
378      gp->linestyle(ss.str());
379      gp->plot(i->second);
380    }
381    ss.str("");
382    ss << get_back(*stat, "all") << " total";
383    gp->command("set key height 0");
384    gp->linetitle(ss.str());
385    gp->linestyle("steps 1");
386    gp->plot(total);
387
388    gp->command("unset multiplot");
389    gp->yrange();
390
391    return filename;
392  }
393
394
395  void Stats::plot_init(const std::string& filename) const
396  {
397    GnuplotFE* gp=GnuplotFE::instance();
398    gp->command("set term png");
399    gp->command("set output '"+filename+"'");
400    gp->command("set xtics nomirror");
401    gp->command("set ytics nomirror");
402    gp->command("set key default");
403    gp->command("set key left Left reverse");
404    gp->command("set multiplot");
405  }
406
407
408  void Stats::plot_summary(const std::string& filename) const
409  {
410    plot_init(filename);
411    GnuplotFE* gp=GnuplotFE::instance();
412    std::vector<u_int> total = get_vector(total_, "all");
413    double yrange_max=1.03*total.back()+1;
414    gp->yrange(yrange_max);
415    std::stringstream ss;
416   
417    ss.str("");
418    std::vector<u_int> x(get_vector(code_, "all"));
419    ss << x.back() << " code";
420    gp->command("set key height 2");
421    gp->linetitle(ss.str());
422    gp->linestyle("steps 2");
423    gp->plot(x);
424
425    ss.str("");
426    x = get_vector(comments_, "all");
427    ss << x.back() << " comment";
428    gp->command("set key height 4");
429    gp->linetitle(ss.str());
430    gp->linestyle("steps 3");
431    gp->plot(x);
432
433    ss.str("");
434    x = get_vector(other_, "all");
435    ss << x.back() << " other";
436    gp->command("set key height 6");
437    gp->linetitle(ss.str());
438    gp->linestyle("steps 4");
439    gp->plot(x);
440
441    ss.str("");
442    ss << total.back() << " total";
443    gp->command("set key height 0");
444    gp->linetitle(ss.str());
445    gp->linestyle("steps 1");
446    gp->plot(total);
447
448    gp->command("unset multiplot");
449    gp->yrange();
450  }
451
452
453  void Stats::print(std::ostream& os) const
454  {
455    os << last_changed_rev() << " ";
456    os << authors().size() << " ";
457
458    std::copy(authors().begin(), authors().end(), 
459              std::ostream_iterator<std::string>(os, "\n"));
460    os << code_cache() << "\n";
461    print(os, code_);
462    os << "\n" << comments_cache() << "\n";
463    print(os, comments_);
464    os << "\n" << other_cache() << "\n";
465    print(os, other_);
466    os << "\n" << total_cache() << "\n";
467    print(os, total_);
468    os << "\n" << end_of_cache() << "\n";
469  }
470
471
472  void Stats::print(std::ostream& os, const Author2Vector& m) const
473  {
474    for (A2VConstIter i(m.begin()); i!=m.end(); ++i){
475      os << i->first << "\n";
476      os << i->second.front() << " ";
477      for (size_t j=1; j<i->second.size(); ++j)
478        if (i->second[j] != i->second[j-1])
479          os << j << " " << i->second[j] - i->second[j-1] << " ";
480      os << last_changed_rev()+1 << " ";
481    }
482  }
483
484  void Stats::reset(void)
485  {
486    total_.clear();
487    code_.clear();
488    comments_.clear();
489    other_.clear();
490    authors_.clear();
491    std::vector<u_int> vec;
492    code_["all"] = vec;
493    comments_["all"] = vec;
494    other_["all"] = vec;
495    total_["all"] = vec;
496  }
497
498
499  Stats& Stats::operator+=(const Stats& rhs)
500  {
501    revision_ = std::max(revision_, rhs.revision_);
502    last_changed_rev_ = std::max(last_changed_rev_, rhs.last_changed_rev_);
503    add_authors(rhs.authors().begin(), rhs.authors().end());
504    map_add(rhs.code_.begin(), rhs.code_.end(), code_);
505    map_add(rhs.comments_.begin(), rhs.comments_.end(), comments_);
506    map_add(rhs.other_.begin(), rhs.other_.end(), other_);
507    map_add(rhs.total_.begin(), rhs.total_.end(), total_);
508    assert(rhs.other_.size());
509    assert(other_.size());
510   
511    return *this;
512  }
513
514
515}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.