source: trunk/lib/Stats.cc @ 1348

Last change on this file since 1348 was 1348, checked in by Peter Johansson, 11 years ago

include 'copyright string' in cache. fixes #496

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 12.5 KB
Line 
1// $Id: Stats.cc 1348 2011-03-26 01:52:14Z peter $
2
3/*
4  Copyright (C) 2005 Peter Johansson
5  Copyright (C) 2006, 2007, 2008, 2009 Jari Häkkinen, Peter Johansson
6  Copyright (C) 2010, 2011 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 3 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 svndigest. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include "Stats.h"
25
26#include "Colors.h"
27#include "Configuration.h"
28#include "Functor.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
47namespace theplu{
48namespace svndigest{
49
50
51  Stats::Stats(const std::string& path)
52    : stats_(std::vector<Author2Vector>(LineTypeParser::total+1))
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    reset();
59
60    std::ostringstream ss;
61    if (const std::vector<std::pair<std::string, std::string> >* 
62        codons = Configuration::instance().codon(path)) {
63      using std::string;
64      typedef std::vector<std::pair<string, string> >::const_iterator citerator;
65      for ( citerator codon=codons->begin(); codon!=codons->end(); ++codon) 
66        ss << codon->first << codon->second;
67    }
68    // avoid including copyright string if default is used
69    if (Configuration::instance().copyright_string() != "Copyright (C)")
70      ss << Configuration::instance().copyright_string();
71    config_code_ = ss.str();
72    // we don't allow newline in codon because we parse the string
73    // with newline and thus want the string to be a one-liner.
74    std::replace(config_code_.begin(), config_code_.end(), '\n', ' ');
75  }
76
77
78  Stats::~Stats(void)
79  {
80  }
81
82 
83  void Stats::add(const std::vector<std::map<std::string, SparseVector> >& data)
84  {
85    // loop over line types
86    for (size_t lt = 0; lt<data.size(); ++lt) {
87      std::map<std::string, SparseVector>::const_iterator iter=data[lt].begin();
88      std::map<std::string, SparseVector>::const_iterator last=data[lt].end();
89      // loop over users
90      for ( ; iter!=last; ++iter) {
91        add_author(iter->first);
92        SumVector tmpvec;
93        accumulate(iter->second, tmpvec);
94        stats_[lt][iter->first] += tmpvec;
95      }
96    }
97  }
98
99
100  /*
101  void Stats::accumulate(std::vector<unsigned int>& vec,
102                         svn_revnum_t rev) const
103  {
104    assert(rev>0);
105    if (vec.empty()){
106      // just to allow call to vec.back() below
107      vec.resize(1,0);
108    }
109    else if (vec.begin()+rev-1 < vec.end())
110      std::partial_sum(vec.begin()+rev-1,vec.end(),vec.begin()+rev-1);
111    // static_cast to remove annoying compiler warning
112    if (vec.size() < static_cast<size_t>(revision()+1))
113      vec.resize(revision()+1, vec.back());
114  }
115  */
116  /*
117  void Stats::accumulate_stats(svn_revnum_t rev)
118  {
119    if (!rev)
120      rev = 1;
121    for (std::set<std::string>::const_iterator iter(authors().begin());
122         iter!=authors().end(); ++iter) {
123      std::vector<unsigned int>& code = code_stats()[*iter];
124      accumulate(code, rev);
125      std::vector<unsigned int>& comments = comment_stats()[*iter];
126      accumulate(comments, rev);
127      std::vector<unsigned int>& other = other_stats()[*iter];
128      accumulate(other, rev);
129      std::vector<unsigned int>& copyright = copyright_stats()[*iter];
130      accumulate(copyright, rev);
131    }
132  }
133  */
134  /*
135  void Stats::add(const std::string& user, svn_revnum_t rev,
136                  LineTypeParser::line_type lt, unsigned int n)
137  {
138    assert(user.size());
139    add_author(user);
140
141    if (lt==LineTypeParser::code)
142      add(code_stats()[user], rev, n);
143    else if (lt==LineTypeParser::comment)
144      add(comment_stats()[user], rev, n);
145    else if (lt==LineTypeParser::other)
146      add(other_stats()[user], rev, n);
147    else if (lt==LineTypeParser::copyright)
148      add(copyright_stats()[user], rev, n);
149  }
150
151  void Stats::add(SumVector& vec, svn_revnum_t rev, unsigned int n)
152  {
153    vec.set(rev, vec[rev] + n);
154  }
155  */
156
157
158  void Stats::add_author(std::string name)
159  {
160    authors_.insert(name);
161  }
162
163
164  void Stats::add_authors(std::set<std::string>::const_iterator first, 
165                          std::set<std::string>::const_iterator last)
166  {
167    authors_.insert(first, last);
168  }
169
170
171  const std::set<std::string>& Stats::authors(void) const
172  {
173    return authors_;
174  }
175
176
177  void Stats::calc_all(void)
178  {
179    for (int lt=0; lt <= 4; ++lt) {
180      stats_[lt]["all"].clear();
181      for (std::map<std::string, SumVector>::iterator i = stats_[lt].begin();
182           i!=stats_[lt].end(); ++i) {
183        stats_[lt]["all"] += i->second;
184      }
185    }
186
187    comment_or_copy_stats()["all"] = comment_stats()["all"];
188    comment_or_copy_stats()["all"] += copyright_stats()["all"];
189
190    total_stats()["all"] = comment_or_copy_stats()["all"];
191    total_stats()["all"] += code_stats()["all"];
192    total_stats()["all"] += other_stats()["all"];
193  }
194
195
196  void Stats::calc_total(void)
197  {
198    for (std::set<std::string>::const_iterator iter(authors().begin());
199         iter!=authors().end(); ++iter) {
200      SumVector& total = total_stats()[*iter];
201      total = code_stats()[*iter];
202      total += comment_stats()[*iter];
203      total += other_stats()[*iter];
204      total += copyright_stats()[*iter];
205    }
206  }
207
208
209  void Stats::calc_comment_or_copy(void)
210  {
211    for (std::set<std::string>::const_iterator iter(authors().begin());
212         iter!=authors().end(); ++iter) {
213      const SumVector& comments = comment_stats()[*iter];
214      const SumVector& copy = copyright_stats()[*iter];
215
216      comment_or_copy_stats()[*iter] = comments;
217      comment_or_copy_stats()[*iter] += copy;
218    }
219
220  }
221
222
223  unsigned int Stats::code(const std::string& user) const
224  {
225    return get_back(code_stats(), user);
226  }
227
228
229  unsigned int Stats::comments(const std::string& user) const
230  {
231    return get_back(comment_or_copy_stats(), user);
232  }
233
234
235  unsigned int Stats::empty(const std::string& user) const
236  {
237    return get_back(other_stats(), user);
238  }
239
240
241  unsigned int Stats::get_back(const Author2Vector& m, std::string user) const
242  {
243    A2VConstIter iter(m.find(std::string(user)));
244    if (iter==m.end() || iter->second.empty()) 
245      return 0;
246    return iter->second.back();
247  }
248
249
250  const SumVector& Stats::get_vector(const Author2Vector& m, 
251                                     const std::string& user) const
252  {
253    A2VConstIter iter(m.find(std::string(user)));
254    if (iter==m.end()) 
255      throw std::runtime_error(user+std::string(" not found i Stats"));
256    return iter->second;
257  }
258
259
260  svn_revnum_t Stats::last_changed_rev(void) const
261  {
262    return last_changed_rev_;
263  }
264
265
266  unsigned int Stats::lines(const std::string& user) const
267  {
268    return get_back(total_stats(), user);
269  }
270
271
272  void Stats::load(std::istream& is, Author2Vector& m)
273  {
274    while (m.size() < authors().size()+1 && is.good()) {
275      std::string name;
276      std::getline(is, name);
277      SparseVector vec;
278      std::string line;
279      std::getline(is, line);
280      std::stringstream ss(line);
281      while (ss.good()) {
282        svn_revnum_t rev=0;
283        unsigned int count=0;
284        ss >> rev;
285        ss >> count;
286        assert(rev<=revision_);
287        if (!count)
288          break;
289        vec.set(rev, count);
290      }
291      SumVector& sumvec = m[name];
292      accumulate(vec, sumvec);
293    }
294  }
295
296
297  svn_revnum_t Stats::load_cache(std::istream& is, bool& latest_ver)
298  {
299    std::string str;
300    getline(is, str);
301    if (str == "CACHE FILE VERSION 8") {
302      latest_ver = true;
303      return load_cache8(is);
304    }
305    latest_ver = false;
306    if (str == "CACHE FILE VERSION 7")
307      return load_cache7(is);
308
309    if (str == "CACHE FILE VERSION 6")
310      std::cout << "cache file is obsolete; "
311                << "retrieving statistics from repository.\n";
312    return 0;
313  }
314
315
316  svn_revnum_t Stats::load_cache8(std::istream& is)
317  {
318    std::string line;
319    getline(is, line);
320    if (line!=config_code_) {
321      std::cout << "cache file is for different configuration.\n"
322                << "config code: '" << config_code_ << "'\n"
323                << "config code in cache file: '" << line << "'\n"
324                << "retrieving statistics from repository.\n";
325      return 0;
326    }
327    return load_cache7(is);
328  }
329
330
331  svn_revnum_t Stats::load_cache7(std::istream& is)
332  {
333    svn_revnum_t rev;
334    is >> rev;
335    reset();
336    size_t a_size=0;
337    is >> a_size;
338    std::string str;
339    while (authors().size()<a_size && is.good()){
340      getline(is, str);
341      if (str.size())
342        add_author(str);
343    }
344    getline(is, str);
345    if (str!=cache_check_str()) {
346      return 0;
347    }
348    for (size_t i=0; i<stats_.size(); ++i){
349      load(is, stats_[i]);
350      getline(is, str);
351      if (str!=cache_check_str()) {
352        return 0;
353      }
354    }
355    return rev;
356  }
357
358
359  void Stats::map_add(A2VConstIter first1, A2VConstIter last1, 
360                      Author2Vector& map)
361  {
362    A2VIter first2(map.begin());
363    Author2Vector::key_compare compare;
364    while ( first1 != last1) { 
365      // key of first1 less than key of first2
366      if (first2==map.end() || compare(first1->first,first2->first)) {
367        first2 = map.insert(first2, *first1);
368        ++first1;
369      }
370      // key of first2 less than key of first1
371      else if ( compare(first2->first, first1->first)) {
372        ++first2;
373      }
374      // keys are equivalent
375      else {
376        first2->second += first1->second;
377        ++first1;
378        ++first2;
379      }
380    }
381  }
382
383
384  unsigned int Stats::max_element(const SumVector& vec) const
385  {
386    return std::max_element(vec.begin(), vec.end(), 
387       pair_value_compare<const svn_revnum_t, unsigned int>())->second;
388  }
389
390
391  void Stats::parse(const std::string& path, svn_revnum_t rev)
392  {
393    do_parse(path, rev);
394    calc_comment_or_copy();
395    calc_total();
396    calc_all();
397   
398    assert(total_stats().size());
399    assert(code_stats().size());
400    assert(comment_or_copy_stats().size());
401    assert(other_stats().size());
402  }
403
404
405  void Stats::print(std::ostream& os) const
406  {
407    // indicate that cache file is version 8 here , but keep remaning
408    // 'CACHE FILE VERSION' as 'VERSION 7' to allow load_cache7() to
409    // be used from load_cache8().
410    os << "CACHE FILE VERSION 8\n";
411    os << config_code_ << "\n";
412    os << last_changed_rev() << " ";
413    os << authors().size() << "\n";
414
415    std::copy(authors().begin(), authors().end(), 
416              std::ostream_iterator<std::string>(os, "\n"));
417    os << cache_check_str() << "\n";
418    for (size_t i=0; i<stats_.size(); ++i){
419      print(os, stats_[i]);
420      os << cache_check_str() << "\n";
421    }
422  }
423
424
425  void Stats::print(std::ostream& os, const Author2Vector& m) const
426  {
427    for (A2VConstIter i(m.begin()); i!=m.end(); ++i){
428      os << i->first << "\n";
429      const SumVector& vec = i->second;
430      if (vec.size()) {
431        SumVector::const_iterator v = vec.begin();
432        if (v->second)
433          os << v->first << " " << v->second << " ";
434        ++v;
435        SumVector::const_iterator prev = vec.begin();
436        while (v != vec.end()) {
437          assert(vec[v->first - 1] == prev->second);
438          // FIXME: this if should not be needed if SumVector was
439          // truly sparse and collapsed when possible.
440          if (v->second != prev->second)
441            os << v->first << " " << v->second - prev->second << " ";
442          ++v;
443          ++prev;
444        }
445      }
446      os << "\n";
447    }
448  }
449
450  void Stats::reset(void)
451  {
452    for (size_t i=0; i<stats_.size(); ++i){
453      stats_[i].clear();
454      stats_[i]["all"].resize(revision()+1);
455    }
456    authors_.clear();
457  }
458
459
460  void Stats::resize(svn_revnum_t rev)
461  {
462    // set size on vectors
463    for (size_t i=0; i<stats_.size(); ++i) {
464      for (A2VIter iter=stats_[i].begin(); iter!=stats_[i].end(); ++iter) {
465        iter->second.resize(rev);
466      }
467    }
468  }
469
470
471  svn_revnum_t Stats::revision(void) const 
472  { 
473    return revision_; 
474  }
475
476
477  Stats& Stats::operator+=(const Stats& rhs)
478  {
479    revision_ = std::max(revision_, rhs.revision_);
480    last_changed_rev_ = std::max(last_changed_rev_, rhs.last_changed_rev_);
481    add_authors(rhs.authors().begin(), rhs.authors().end());
482    assert(stats_.size()==rhs.stats_.size());
483    for (size_t i=0; i<stats_.size(); ++i)
484      map_add(rhs.stats_[i].begin(), rhs.stats_[i].end(), stats_[i]);
485   
486    return *this;
487  }
488
489 
490  size_t Stats::operator()(int linetype, std::string author, 
491                           svn_revnum_t rev) const
492  {
493    assert(linetype<=LineTypeParser::total);
494    assert(static_cast<size_t>(linetype) < stats_.size());
495    assert(rev>=0);
496    A2VConstIter i = stats_[linetype].find(author);
497    if (i==stats_[linetype].end()){
498      std::stringstream msg;
499      msg << __FILE__ << ": author: " << author << " does not exist"; 
500      throw std::runtime_error(msg.str());
501    }
502    assert(rev <= revision());
503    //    assert(rev < static_cast<svn_revnum_t>(i->second.size()));
504    return i->second[rev];
505  }
506
507}} // end of namespace svndigest and namespace theplu
Note: See TracBrowser for help on using the repository browser.