source: trunk/lib/Stats.cc @ 529

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

moving stuff to base class, Stats

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