source: trunk/lib/Stats.cc @ 151

Last change on this file since 151 was 149, checked in by Jari Häkkinen, 15 years ago

Changed svnstat to svndigest.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 6.2 KB
Line 
1// $Id: Stats.cc 149 2006-08-12 09:11:46Z jari $
2
3/*
4  Copyright (C) 2005 Peter Johansson
5  Copyright (C) 2006 Jari Häkkinen, Peter Johansson
6
7  This file is part of svndigest, http://lev.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 <iostream>
36#include <map>
37#include <numeric>
38#include <string>
39#include <sstream>
40#include <unistd.h>
41#include <utility>
42#include <vector>
43
44
45namespace theplu{
46namespace svndigest{
47
48
49  Stats::Stats(const std::string& path)
50  {
51    // Make sure latest revision is set properly
52    SVNinfo svn_info(path);
53    revision_=svn_info.rev();
54    last_changed_rev_=svn_info.last_changed_rev();
55  }
56
57
58  std::vector<u_int> Stats::accumulated(const Map_& map) const
59  {
60    // sum of all users
61    std::vector<u_int> sum(revision_+1);
62    sum=std::accumulate(map.begin(), map.end(), sum,
63                        PairValuePlus<std::string,u_int>());
64
65    // calculate accumulated sum
66    std::vector<u_int> accum(sum.size());
67    std::partial_sum(sum.begin(),sum.end(),accum.begin());
68    assert(sum.size()==accum.size());
69    return accum;
70  }
71
72  std::vector<u_int> Stats::accumulated(const Map_& map, 
73                                        const std::string& user) const
74  {
75    if (!map.count(user))
76      return std::vector<u_int>();
77    std::vector<u_int> vec=(map.find(user))->second;
78
79    // static_cast to remove annoying compiler warning
80    if (vec.size() < static_cast<size_t>(revision_+1))
81      vec.insert(vec.end(), revision_+1-vec.size(), 0);
82
83    std::vector<u_int> accum(vec.size());
84    std::partial_sum(vec.begin(),vec.end(),accum.begin());
85    return accum;
86  }
87
88  void Stats::add(const std::string& user, const u_int& rev, 
89                  const Parser::line_type& lt)
90  {
91    authors_.insert(user);
92
93    std::vector<u_int>* total = &(total_[user]);
94    if (total->size() < rev+1){
95      total->reserve(revision_ + 1);
96      total->insert(total->end(), rev - total->size(), 0);
97      total->push_back(1);
98    }
99    else
100      (*total)[rev]++;
101
102    std::vector<u_int>* code = &(code_[user]);
103    if (code->size() < rev+1){
104      code->reserve(revision_ + 1);
105      code->insert(code->end(), rev - code->size(), 0);
106      if (lt == Parser::code)
107        code->push_back(1);
108      else 
109        code->push_back(0);
110    }
111    else if (lt == Parser::code)
112      (*code)[rev]++;
113
114    std::vector<u_int>* comments = &(comments_[user]);
115    if (comments->size() < rev+1){
116      comments->reserve(revision_ + 1);
117      comments->insert(comments->end(), rev - comments->size(), 0);
118      if (lt == Parser::comment)
119        comments->push_back(1);
120      else 
121        comments->push_back(0);
122    }
123    else if (lt == Parser::comment)
124      (*comments)[rev]++;
125  }
126
127
128  bool Stats::parse(const std::string& path)
129  {
130    SVNblame svn_blame(path);
131    if (svn_blame.binary())
132      return true;
133
134    Parser parser(path);
135    std::vector<Parser::line_type>::const_iterator count=parser.type().begin();
136
137    while (const SVNblame::blame_information * bi=svn_blame.next()) {
138      if (!bi->line.size()) { // skip empty line
139        ++count; // keep Parser information in sync with Stats parsing
140        continue;
141      }
142      // to handle symbolic links
143      if (count==parser.type().end())
144        add(bi->author, bi->revision, Parser::empty);
145      else 
146        add(bi->author, bi->revision, *count);
147      count++;
148    }
149   
150    return false;
151  }
152
153
154  std::string Stats::plot(const std::string& filename,
155                          const std::string& title) const
156  {
157    GnuplotFE* gp=GnuplotFE::instance();
158    gp->command("set term png transparent");
159    gp->command("set output '"+filename+"'");
160    gp->command("set title '"+title+"'");
161    gp->command("set xtics nomirror");
162    gp->command("set ytics nomirror");
163    gp->command("set key default");
164    gp->command("set key left Left reverse");
165    gp->command("set multiplot");
166    std::vector<u_int> total=accumulated(total_);   
167    double yrange_max=1.03*total.back()+1;
168    gp->yrange(yrange_max);
169    size_t plotno=1;
170    std::stringstream ss;
171    for (MapConstIter_ i= total_.begin(); i != total_.end(); i++) {
172      ss.str("");
173      ss << "set key height " << 2*plotno;
174      gp->command(ss.str());
175      std::vector<u_int> x=accumulated(total_, i->first);
176      ss.str("");
177      ss << x.back() << " " << i->first;
178      gp->yrange(yrange_max);
179      gp->linetitle(ss.str());
180      ss.str("");
181      ss << "steps " << ++plotno;
182      gp->linestyle(ss.str());
183      gp->plot(x);
184    }
185    ss.str("");
186    ss << total.back() << " total";
187    gp->command("set key height 0");
188    gp->linetitle(ss.str());
189    gp->linestyle("steps 1");
190    gp->plot(total);
191
192    gp->command("unset multiplot");
193    gp->yrange();
194
195    return filename;
196  }
197
198
199  Stats& Stats::operator+=(const Stats& other)
200  {
201    for (MapConstIter_ o_i= other.code_.begin(); 
202         o_i != other.code_.end(); ++o_i)
203    {
204      std::pair<MapIter_,bool> result = code_.insert(*o_i);
205      if (!result.second)
206        code_[(*(result.first)).first] = 
207          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
208 
209    }
210 
211    for (MapConstIter_ o_i= other.comments_.begin(); 
212         o_i != other.comments_.end(); ++o_i)
213    {
214      std::pair<MapIter_,bool> result = comments_.insert(*o_i);
215      if (!result.second)
216        comments_[(*(result.first)).first] = 
217          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
218 
219    }
220   
221    for (MapConstIter_ o_i= other.total_.begin(); 
222         o_i != other.total_.end(); ++o_i)
223    {
224      std::pair<MapIter_,bool> result = total_.insert(*o_i);
225      if (!result.second)
226        total_[(*(result.first)).first] = 
227          VectorPlus<u_int>()( (*(result.first)).second, (*o_i).second );
228 
229    }
230   
231    if (!other.authors().empty())
232      authors_.insert(other.authors().begin(), other.authors().end());
233    return *this;
234  }
235
236}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.