source: trunk/lib/Stats.cc @ 468

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

Added load function in Stats class. refs #78. This ticket is hereby in principle completed. However, I would not trust the implementation, but the functions, Stats::load and Stats::print, should be tested. At minimum they should work together.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.7 KB
Line 
1// $Id: Stats.cc 468 2007-08-30 18:11:12Z 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    authors_.insert(user);
95
96    std::vector<u_int>* total = &(total_[user]);
97    assert(total);
98    if (total->size() < rev+1){
99      total->reserve(revision_ + 1);
100      total->insert(total->end(), rev - total->size(), 0);
101      total->push_back(1);
102    }
103    else
104      ++(*total)[rev];
105
106    std::vector<u_int>* code = &(code_[user]);
107    assert(code);
108    if (code->size() < rev+1){
109      code->reserve(revision_ + 1);
110      code->insert(code->end(), rev - code->size(), 0);
111      if (lt == Parser::code)
112        code->push_back(1);
113      else 
114        code->push_back(0);
115    }
116    else if (lt == Parser::code)
117      ++(*code)[rev];
118
119    std::vector<u_int>* comments = &(comments_[user]);
120    assert(comments);
121    if (comments->size() < rev+1){
122      comments->reserve(revision_ + 1);
123      comments->insert(comments->end(), rev - comments->size(), 0);
124      if (lt == Parser::comment)
125        comments->push_back(1);
126      else 
127        comments->push_back(0);
128    }
129    else if (lt == Parser::comment)
130      ++(*comments)[rev];
131
132    std::vector<u_int>* empty = &(empty_[user]);
133    assert(empty);
134    if (empty->size() < rev+1){
135      empty->reserve(revision_ + 1);
136      empty->insert(empty->end(), rev - empty->size(), 0);
137      if (lt == Parser::empty)
138        empty->push_back(1);
139      else 
140        empty->push_back(0);
141    }
142    else if (lt == Parser::empty)
143      ++(*empty)[rev];
144  }
145
146
147  void Stats::load(std::istream& is)
148  {
149    is >> last_changed_rev_;
150    size_t a_size=0;
151    authors_.clear();
152    is >> a_size;
153    std::string str;
154    while (authors_.size()<a_size){
155      getline(is, str);
156      authors_.insert(str);
157    }
158    load(is, code_);
159    load(is, comments_);
160    load(is, empty_);
161    load(is, total_);
162  }
163
164
165  void Stats::load(std::istream& is, Map_& m)
166  {
167    m.clear();
168    while (m.size() < authors_.size()) {
169      std::string name;
170      std::getline(is, name);
171      std::vector<u_int>& vec=m[name];
172      size_t revs=0;
173      is >> revs;
174      vec.reserve(revs);
175      while (vec.size() < revs) {
176        u_int tmp;
177        is >> tmp;
178        vec.push_back(tmp);
179      }
180    }
181  }
182
183
184  void Stats::parse(const std::string& path)
185  {
186    Parser parser(path);
187    std::vector<Parser::line_type>::const_iterator count=parser.type().begin();
188
189    SVNblame svn_blame(path);
190    while (svn_blame.valid()) {
191      add(svn_blame.author(), svn_blame.revision(), *count);
192      svn_blame.next_line();
193      ++count;
194    }
195   
196  }
197
198
199  std::string Stats::plot(const std::string& filename,
200                          const std::string& linetype) const
201  {
202    plot_init(filename);
203    GnuplotFE* gp=GnuplotFE::instance();
204    const Map_* stat=NULL;
205    if (linetype=="total")
206      stat = &total_;
207    else if (linetype=="code")
208      stat = &code_;
209    else if (linetype=="comments")
210      stat = &comments_;
211    else if (linetype=="empty")
212      stat = &empty_;
213    assert(stat);
214    std::vector<u_int> total=accumulated(*stat);   
215    double yrange_max=1.03*total.back()+1;
216    gp->yrange(yrange_max);
217
218    typedef std::vector<std::pair<std::string, std::vector<u_int> > > vec_type;
219    vec_type author_cont;
220    author_cont.reserve(stat->size());
221    for (MapConstIter_ i= stat->begin(); i != stat->end(); ++i) {
222      author_cont.push_back(std::make_pair(i->first,
223                                           accumulated(*stat,i->first)));
224    }
225
226    LessReversed<std::vector<u_int> > lr;
227    PairSecondCompare<std::string, std::vector<u_int>, 
228      LessReversed<std::vector<u_int> > > compare(lr);
229    std::sort(author_cont.begin(), author_cont.end(), compare);
230
231    size_t plotno=author_cont.size();
232    std::stringstream ss;
233    vec_type::iterator end(author_cont.end());
234    for (vec_type::iterator i(author_cont.begin()); i!=end; ++i) {
235      ss.str("");
236      ss << "set key height " << 2*plotno;
237      gp->command(ss.str());
238      ss.str("");
239      ss << i->second.back() << " " << i->first;
240      gp->yrange(yrange_max);
241      gp->linetitle(ss.str());
242      ss.str("");
243      ss << "steps " << --plotno+2;
244      gp->linestyle(ss.str());
245      gp->plot(i->second);
246    }
247    ss.str("");
248    ss << total.back() << " total";
249    gp->command("set key height 0");
250    gp->linetitle(ss.str());
251    gp->linestyle("steps 1");
252    gp->plot(total);
253
254    gp->command("unset multiplot");
255    gp->yrange();
256
257    return filename;
258  }
259
260
261  void Stats::plot_init(const std::string& filename) const
262  {
263    GnuplotFE* gp=GnuplotFE::instance();
264    gp->command("set term png");
265    gp->command("set output '"+filename+"'");
266    gp->command("set xtics nomirror");
267    gp->command("set ytics nomirror");
268    gp->command("set key default");
269    gp->command("set key left Left reverse");
270    gp->command("set multiplot");
271  }
272
273
274  void Stats::plot_summary(const std::string& filename) const
275  {
276    plot_init(filename);
277    GnuplotFE* gp=GnuplotFE::instance();
278    std::vector<u_int> total=accumulated(total_);   
279    double yrange_max=1.03*total.back()+1;
280    gp->yrange(yrange_max);
281    std::stringstream ss;
282   
283    ss.str("");
284    std::vector<u_int> x=accumulated(code_);   
285    ss << x.back() << " code";
286    gp->command("set key height 2");
287    gp->linetitle(ss.str());
288    gp->linestyle("steps 2");
289    gp->plot(x);
290
291    ss.str("");
292    x=accumulated(comments_);   
293    ss << x.back() << " comment";
294    gp->command("set key height 4");
295    gp->linetitle(ss.str());
296    gp->linestyle("steps 3");
297    gp->plot(x);
298
299    ss.str("");
300    x=accumulated(empty_);   
301    ss << x.back() << " other";
302    gp->command("set key height 6");
303    gp->linetitle(ss.str());
304    gp->linestyle("steps 4");
305    gp->plot(x);
306
307    ss.str("");
308    ss << total.back() << " total";
309    gp->command("set key height 0");
310    gp->linetitle(ss.str());
311    gp->linestyle("steps 1");
312    gp->plot(total);
313
314    gp->command("unset multiplot");
315    gp->yrange();
316  }
317
318
319  void Stats::print(std::ostream& os) const
320  {
321    os << last_changed_rev_ << "\n";
322    os << authors_.size() << "\n";
323    std::copy(authors_.begin(), authors_.end(), 
324              std::ostream_iterator<std::string>(os, "\n"));
325    print(os, code_);
326    print(os, comments_);
327    print(os, empty_);
328    print(os, total_);
329  }
330
331
332  void Stats::print(std::ostream& os, const Map_& m) const
333  {
334    for (MapConstIter_ i(m.begin()); i!=m.end(); ++i){
335      os << i->first << "\n";
336      os << i->second.size() << "\n";
337      std::copy(i->second.begin(), i->second.end(),
338                std::ostream_iterator<u_int>(os, "\n"));
339    }
340  }
341
342
343  Stats& Stats::operator+=(const Stats& other)
344  {
345    for (MapConstIter_ o_i= other.code_.begin(); 
346         o_i != other.code_.end(); ++o_i)
347    {
348      std::pair<MapIter_,bool> result = code_.insert(*o_i);
349      if (!result.second)
350        code_[(*(result.first)).first] = 
351          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
352 
353    }
354 
355    for (MapConstIter_ o_i= other.comments_.begin(); 
356         o_i != other.comments_.end(); ++o_i)
357    {
358      std::pair<MapIter_,bool> result = comments_.insert(*o_i);
359      if (!result.second)
360        comments_[(*(result.first)).first] = 
361          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
362 
363    }
364   
365    for (MapConstIter_ o_i= other.empty_.begin(); 
366         o_i != other.empty_.end(); ++o_i)
367    {
368      std::pair<MapIter_,bool> result = empty_.insert(*o_i);
369      if (!result.second)
370        empty_[(*(result.first)).first] = 
371          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
372 
373    }
374   
375    for (MapConstIter_ o_i= other.total_.begin(); 
376         o_i != other.total_.end(); ++o_i)
377    {
378      std::pair<MapIter_,bool> result = total_.insert(*o_i);
379      if (!result.second)
380        total_[(*(result.first)).first] = 
381          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
382 
383    }
384   
385    if (!other.authors().empty())
386      authors_.insert(other.authors().begin(), other.authors().end());
387    return *this;
388  }
389
390}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.