source: trunk/lib/Stats.cc @ 454

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

adding print function in Stats class - refs #78

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.1 KB
Line 
1// $Id: Stats.cc 454 2007-08-18 00:01:45Z 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 "GnuplotFE.h"
28#include "SVNblame.h"
29#include "SVNinfo.h"
30#include "utility.h"
31
32#include <algorithm>
33#include <cassert>
34#include <cstdlib>
35#include <fstream>
36#include <iostream>
37#include <iterator>
38#include <map>
39#include <numeric>
40#include <string>
41#include <sstream>
42#include <unistd.h>
43#include <utility>
44#include <vector>
45
46
47namespace theplu{
48namespace svndigest{
49
50
51  Stats::Stats(const std::string& path)
52  {
53    // Make sure latest revision is set properly
54    SVNinfo svn_info(path);
55    revision_=svn_info.rev();
56    last_changed_rev_=svn_info.last_changed_rev();
57  }
58
59
60  std::vector<u_int> Stats::accumulated(const Map_& map) const
61  {
62    // sum of all users
63    std::vector<u_int> sum(revision_+1);
64    sum=std::accumulate(map.begin(), map.end(), sum,
65                        PairValuePlus<std::string,u_int>());
66
67    // calculate accumulated sum
68    std::vector<u_int> accum(sum.size());
69    std::partial_sum(sum.begin(),sum.end(),accum.begin());
70    assert(sum.size()==accum.size());
71    return accum;
72  }
73
74  std::vector<u_int> Stats::accumulated(const Map_& map, 
75                                        const std::string& user) const
76  {
77    if (!map.count(user))
78      return std::vector<u_int>(last_changed_rev_,0);
79    std::vector<u_int> vec=(map.find(user))->second;
80
81    // static_cast to remove annoying compiler warning
82    if (vec.size() < static_cast<size_t>(revision_+1))
83      vec.insert(vec.end(), revision_+1-vec.size(), 0);
84
85    std::vector<u_int> accum(vec.size());
86    std::partial_sum(vec.begin(),vec.end(),accum.begin());
87    return accum;
88  }
89
90  void Stats::add(const std::string& user, const u_int& rev, 
91                  const Parser::line_type& lt)
92  {
93    authors_.insert(user);
94
95    std::vector<u_int>* total = &(total_[user]);
96    assert(total);
97    if (total->size() < rev+1){
98      total->reserve(revision_ + 1);
99      total->insert(total->end(), rev - total->size(), 0);
100      total->push_back(1);
101    }
102    else
103      ++(*total)[rev];
104
105    std::vector<u_int>* code = &(code_[user]);
106    assert(code);
107    if (code->size() < rev+1){
108      code->reserve(revision_ + 1);
109      code->insert(code->end(), rev - code->size(), 0);
110      if (lt == Parser::code)
111        code->push_back(1);
112      else 
113        code->push_back(0);
114    }
115    else if (lt == Parser::code)
116      ++(*code)[rev];
117
118    std::vector<u_int>* comments = &(comments_[user]);
119    assert(comments);
120    if (comments->size() < rev+1){
121      comments->reserve(revision_ + 1);
122      comments->insert(comments->end(), rev - comments->size(), 0);
123      if (lt == Parser::comment)
124        comments->push_back(1);
125      else 
126        comments->push_back(0);
127    }
128    else if (lt == Parser::comment)
129      ++(*comments)[rev];
130
131    std::vector<u_int>* empty = &(empty_[user]);
132    assert(empty);
133    if (empty->size() < rev+1){
134      empty->reserve(revision_ + 1);
135      empty->insert(empty->end(), rev - empty->size(), 0);
136      if (lt == Parser::empty)
137        empty->push_back(1);
138      else 
139        empty->push_back(0);
140    }
141    else if (lt == Parser::empty)
142      ++(*empty)[rev];
143  }
144
145
146  void Stats::parse(const std::string& path)
147  {
148    Parser parser(path);
149    std::vector<Parser::line_type>::const_iterator count=parser.type().begin();
150
151    SVNblame svn_blame(path);
152    while (svn_blame.valid()) {
153      add(svn_blame.author(), svn_blame.revision(), *count);
154      svn_blame.next_line();
155      ++count;
156    }
157   
158  }
159
160
161  std::string Stats::plot(const std::string& filename,
162                          const std::string& linetype) const
163  {
164    plot_init(filename);
165    GnuplotFE* gp=GnuplotFE::instance();
166    const Map_* stat=NULL;
167    if (linetype=="total")
168      stat = &total_;
169    else if (linetype=="code")
170      stat = &code_;
171    else if (linetype=="comments")
172      stat = &comments_;
173    else if (linetype=="empty")
174      stat = &empty_;
175    assert(stat);
176    std::vector<u_int> total=accumulated(*stat);   
177    double yrange_max=1.03*total.back()+1;
178    gp->yrange(yrange_max);
179
180    typedef std::vector<std::pair<std::string, std::vector<u_int> > > vec_type;
181    vec_type author_cont;
182    author_cont.reserve(stat->size());
183    for (MapConstIter_ i= stat->begin(); i != stat->end(); ++i) {
184      author_cont.push_back(std::make_pair(i->first,
185                                           accumulated(*stat,i->first)));
186    }
187
188    LessReversed<std::vector<u_int> > lr;
189    PairSecondCompare<std::string, std::vector<u_int>, 
190      LessReversed<std::vector<u_int> > > compare(lr);
191    std::sort(author_cont.begin(), author_cont.end(), compare);
192
193    size_t plotno=author_cont.size();
194    std::stringstream ss;
195    vec_type::iterator end(author_cont.end());
196    for (vec_type::iterator i(author_cont.begin()); i!=end; ++i) {
197      ss.str("");
198      ss << "set key height " << 2*plotno;
199      gp->command(ss.str());
200      ss.str("");
201      ss << i->second.back() << " " << i->first;
202      gp->yrange(yrange_max);
203      gp->linetitle(ss.str());
204      ss.str("");
205      ss << "steps " << --plotno+2;
206      gp->linestyle(ss.str());
207      gp->plot(i->second);
208    }
209    ss.str("");
210    ss << total.back() << " total";
211    gp->command("set key height 0");
212    gp->linetitle(ss.str());
213    gp->linestyle("steps 1");
214    gp->plot(total);
215
216    gp->command("unset multiplot");
217    gp->yrange();
218
219    return filename;
220  }
221
222
223  void Stats::plot_init(const std::string& filename) const
224  {
225    GnuplotFE* gp=GnuplotFE::instance();
226    gp->command("set term png");
227    gp->command("set output '"+filename+"'");
228    gp->command("set xtics nomirror");
229    gp->command("set ytics nomirror");
230    gp->command("set key default");
231    gp->command("set key left Left reverse");
232    gp->command("set multiplot");
233  }
234
235
236  void Stats::plot_summary(const std::string& filename) const
237  {
238    plot_init(filename);
239    GnuplotFE* gp=GnuplotFE::instance();
240    std::vector<u_int> total=accumulated(total_);   
241    double yrange_max=1.03*total.back()+1;
242    gp->yrange(yrange_max);
243    std::stringstream ss;
244   
245    ss.str("");
246    std::vector<u_int> x=accumulated(code_);   
247    ss << x.back() << " code";
248    gp->command("set key height 2");
249    gp->linetitle(ss.str());
250    gp->linestyle("steps 2");
251    gp->plot(x);
252
253    ss.str("");
254    x=accumulated(comments_);   
255    ss << x.back() << " comment";
256    gp->command("set key height 4");
257    gp->linetitle(ss.str());
258    gp->linestyle("steps 3");
259    gp->plot(x);
260
261    ss.str("");
262    x=accumulated(empty_);   
263    ss << x.back() << " other";
264    gp->command("set key height 6");
265    gp->linetitle(ss.str());
266    gp->linestyle("steps 4");
267    gp->plot(x);
268
269    ss.str("");
270    ss << total.back() << " total";
271    gp->command("set key height 0");
272    gp->linetitle(ss.str());
273    gp->linestyle("steps 1");
274    gp->plot(total);
275
276    gp->command("unset multiplot");
277    gp->yrange();
278  }
279
280
281  void Stats::print(std::ostream& os) const
282  {
283    os << last_changed_rev_ << "\n";
284    os << authors_.size() << "\n";
285    std::copy(authors_.begin(), authors_.end(), 
286              std::ostream_iterator<std::string>(os, "\n"));
287    print(os, code_);
288    print(os, comments_);
289    print(os, empty_);
290    print(os, total_);
291  }
292
293
294  void Stats::print(std::ostream& os, const Map_& m) const
295  {
296    for (MapConstIter_ i(m.begin()); i!=m.end(); ++i){
297      os << i->first << "\n";
298      os << i->second.size() << "\n";
299      std::copy(i->second.begin(), i->second.end(),
300                std::ostream_iterator<u_int>(os, "\n"));
301    }
302  }
303
304
305  Stats& Stats::operator+=(const Stats& other)
306  {
307    for (MapConstIter_ o_i= other.code_.begin(); 
308         o_i != other.code_.end(); ++o_i)
309    {
310      std::pair<MapIter_,bool> result = code_.insert(*o_i);
311      if (!result.second)
312        code_[(*(result.first)).first] = 
313          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
314 
315    }
316 
317    for (MapConstIter_ o_i= other.comments_.begin(); 
318         o_i != other.comments_.end(); ++o_i)
319    {
320      std::pair<MapIter_,bool> result = comments_.insert(*o_i);
321      if (!result.second)
322        comments_[(*(result.first)).first] = 
323          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
324 
325    }
326   
327    for (MapConstIter_ o_i= other.empty_.begin(); 
328         o_i != other.empty_.end(); ++o_i)
329    {
330      std::pair<MapIter_,bool> result = empty_.insert(*o_i);
331      if (!result.second)
332        empty_[(*(result.first)).first] = 
333          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
334 
335    }
336   
337    for (MapConstIter_ o_i= other.total_.begin(); 
338         o_i != other.total_.end(); ++o_i)
339    {
340      std::pair<MapIter_,bool> result = total_.insert(*o_i);
341      if (!result.second)
342        total_[(*(result.first)).first] = 
343          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
344 
345    }
346   
347    if (!other.authors().empty())
348      authors_.insert(other.authors().begin(), other.authors().end());
349    return *this;
350  }
351
352}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.