source: tags/0.6.8/lib/Stats.cc

Last change on this file was 731, checked in by Peter Johansson, 13 years ago

update copyright statements

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