gitstats-rb 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/LICENSE +674 -0
  4. data/README.md +74 -0
  5. data/bin/gitstats +200 -0
  6. data/gitstats-rb.gemspec +24 -0
  7. data/lib/gitstats.rb +18 -0
  8. data/lib/gitstats/author.rb +37 -0
  9. data/lib/gitstats/git.rb +165 -0
  10. data/lib/gitstats/renderer.rb +30 -0
  11. data/lib/gitstats/renderer/gnuplot.rb +150 -0
  12. data/lib/gitstats/renderer/haml.rb +72 -0
  13. data/lib/gitstats/renderer/sass.rb +33 -0
  14. data/lib/gitstats/statgen.rb +119 -0
  15. data/lib/gitstats/stats.rb +12 -0
  16. data/lib/gitstats/stats/commit.rb +63 -0
  17. data/lib/gitstats/stats/commit/author.rb +17 -0
  18. data/lib/gitstats/stats/commit/time.rb +109 -0
  19. data/lib/gitstats/stats/file.rb +15 -0
  20. data/lib/gitstats/stats/file/filetype.rb +14 -0
  21. data/lib/gitstats/yearmonth.rb +39 -0
  22. data/template/gnuplot/activity.haml +3 -0
  23. data/template/gnuplot/asc.gif +0 -0
  24. data/template/gnuplot/authors.haml +2 -0
  25. data/template/gnuplot/bg.gif +0 -0
  26. data/template/gnuplot/commits_per_hour.plot +14 -0
  27. data/template/gnuplot/commits_per_month.plot +15 -0
  28. data/template/gnuplot/commits_per_wday.plot +15 -0
  29. data/template/gnuplot/commits_per_year.plot +11 -0
  30. data/template/gnuplot/commits_per_yearmonth.plot +11 -0
  31. data/template/gnuplot/desc.gif +0 -0
  32. data/template/gnuplot/filechanges_by_yearmonth.plot +17 -0
  33. data/template/gnuplot/files.haml +2 -0
  34. data/template/gnuplot/files_by_yearmonth.plot +11 -0
  35. data/template/gnuplot/helpers/block.rb +26 -0
  36. data/template/gnuplot/helpers/config.rb +12 -0
  37. data/template/gnuplot/helpers/names.rb +79 -0
  38. data/template/gnuplot/helpers/utils.rb +16 -0
  39. data/template/gnuplot/index.haml +2 -0
  40. data/template/gnuplot/jqplot.canvasAxisLabelRenderer.min.js +57 -0
  41. data/template/gnuplot/jqplot.canvasAxisTickRenderer.min.js +57 -0
  42. data/template/gnuplot/jqplot.canvasTextRenderer.min.js +57 -0
  43. data/template/gnuplot/jqplot.cursor.min.js +57 -0
  44. data/template/gnuplot/jqplot.dateAxisRenderer.min.js +57 -0
  45. data/template/gnuplot/jquery.jqplot.min.css +1 -0
  46. data/template/gnuplot/jquery.jqplot.min.js +57 -0
  47. data/template/gnuplot/jquery.js +4 -0
  48. data/template/gnuplot/jquery.tablesorter.js +4 -0
  49. data/template/gnuplot/lastweeks.plot +14 -0
  50. data/template/gnuplot/layouts/default.haml +117 -0
  51. data/template/gnuplot/linechanges_by_yearmonth.plot +17 -0
  52. data/template/gnuplot/lines.haml +2 -0
  53. data/template/gnuplot/lines_by_yearmonth.plot +11 -0
  54. data/template/gnuplot/partials/authors.haml +30 -0
  55. data/template/gnuplot/partials/blockheader.haml +2 -0
  56. data/template/gnuplot/partials/blocktoc.haml +7 -0
  57. data/template/gnuplot/partials/commitchart.haml +60 -0
  58. data/template/gnuplot/partials/commits_per_month.haml +27 -0
  59. data/template/gnuplot/partials/commits_per_year.haml +27 -0
  60. data/template/gnuplot/partials/commits_per_yearmonth.haml +27 -0
  61. data/template/gnuplot/partials/day_of_week.haml +28 -0
  62. data/template/gnuplot/partials/filechanges_by_yearmonth.haml +3 -0
  63. data/template/gnuplot/partials/files_by_yearmonth.haml +3 -0
  64. data/template/gnuplot/partials/fileschart.haml +86 -0
  65. data/template/gnuplot/partials/filetypes.haml +23 -0
  66. data/template/gnuplot/partials/general.haml +34 -0
  67. data/template/gnuplot/partials/hour_of_day.haml +28 -0
  68. data/template/gnuplot/partials/hour_of_week.haml +24 -0
  69. data/template/gnuplot/partials/lastweeks.haml +24 -0
  70. data/template/gnuplot/partials/linechanges_by_yearmonth.haml +3 -0
  71. data/template/gnuplot/partials/lines_by_yearmonth.haml +3 -0
  72. data/template/gnuplot/partials/lineschart.haml +86 -0
  73. data/template/gnuplot/partials/repos.haml +13 -0
  74. data/template/gnuplot/partials/top_authors_of_year.haml +30 -0
  75. data/template/gnuplot/partials/top_authors_of_yearmonth.haml +30 -0
  76. data/template/gnuplot/style.scss +132 -0
  77. data/template/highcharts/activity.haml +5 -0
  78. data/template/highcharts/authors.haml +3 -0
  79. data/template/highcharts/bootstrap-theme.min.css +10 -0
  80. data/template/highcharts/bootstrap.min.css +10 -0
  81. data/template/highcharts/bootstrap.min.js +11 -0
  82. data/template/highcharts/exporting.js +23 -0
  83. data/template/highcharts/glyphicons-halflings-regular.eot +0 -0
  84. data/template/highcharts/glyphicons-halflings-regular.svg +229 -0
  85. data/template/highcharts/glyphicons-halflings-regular.ttf +0 -0
  86. data/template/highcharts/glyphicons-halflings-regular.woff +0 -0
  87. data/template/highcharts/heatmap.js +23 -0
  88. data/template/highcharts/helpers/block.rb +27 -0
  89. data/template/highcharts/helpers/names.rb +62 -0
  90. data/template/highcharts/helpers/params.rb +35 -0
  91. data/template/highcharts/highcharts.js +308 -0
  92. data/template/highcharts/index.haml +2 -0
  93. data/template/highcharts/jquery-1.11.1.min.js +4 -0
  94. data/template/highcharts/jquery.dataTables.min.css +1 -0
  95. data/template/highcharts/jquery.dataTables.min.js +164 -0
  96. data/template/highcharts/layouts/default.haml +54 -0
  97. data/template/highcharts/partials/activity_per_day.haml +65 -0
  98. data/template/highcharts/partials/activity_per_hour.haml +42 -0
  99. data/template/highcharts/partials/activity_per_month.haml +47 -0
  100. data/template/highcharts/partials/activity_per_week_last_year.haml +47 -0
  101. data/template/highcharts/partials/activity_per_weekday.haml +44 -0
  102. data/template/highcharts/partials/activity_per_weekday_and_hour.haml +20 -0
  103. data/template/highcharts/partials/blocktoc.haml +4 -0
  104. data/template/highcharts/partials/commits_per_author.haml +64 -0
  105. data/template/highcharts/partials/commits_per_author_detailed.haml +92 -0
  106. data/template/highcharts/partials/general.haml +33 -0
  107. data/template/highcharts/partials/globalstats.haml +41 -0
  108. data/template/highcharts/partials/repos.haml +12 -0
  109. data/template/highcharts/set_highcharts.js +323 -0
  110. data/template/highcharts/style.scss +148 -0
  111. metadata +227 -0
@@ -0,0 +1,30 @@
1
+ class Renderer
2
+ def self.register(cls)
3
+ @@renderers ||= Array.new
4
+ @@renderers << cls
5
+ end
6
+
7
+ def initialize(templatedir, outdir, verbose)
8
+ @templatedir = templatedir
9
+ @outdir = outdir
10
+ @verbose = verbose
11
+ @renderers = @@renderers.map { |x| x.new(templatedir, outdir, verbose) }
12
+ end
13
+
14
+ def render(stats)
15
+ Dir.chdir(@templatedir) { Dir.glob('*').sort }.each do |file|
16
+ next unless File.file?(File.join(@templatedir, file))
17
+
18
+ r = @renderers.find { |r| r.handle?(file) }
19
+
20
+ if r.nil?
21
+ puts "copying '#{file}' ..." if @verbose
22
+ FileUtils.copy(File.join(@templatedir, file), @outdir)
23
+ else
24
+ puts "rendering '#{file}' using #{r.name} ..." if @verbose
25
+ r.render(file, stats)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,150 @@
1
+ class GnuplotRenderer
2
+ class PlotHelper
3
+ class Plotter
4
+ attr_reader :plot
5
+
6
+ def initialize(helper)
7
+ @helper = helper
8
+ @plot = nil
9
+ end
10
+
11
+ def run
12
+ Gnuplot.open do |gp|
13
+ Gnuplot::Plot.new(gp) do |plot|
14
+ @plot = plot
15
+
16
+ plot.terminal 'png transparent size 640,240'
17
+ plot.size '1.0,1.0'
18
+ plot.output File.join(@helper.outdir, File.basename(@helper.filename, '.plot') + '.png')
19
+ plot.nokey
20
+ plot.xtics 'rotate'
21
+ plot.ytics 'autofreq'
22
+ plot.grid 'y'
23
+
24
+ yield self
25
+ end
26
+ end
27
+ end
28
+
29
+ def add_boxes(args = {})
30
+ args = {
31
+ :setrange => true,
32
+ :limitlabels => true,
33
+ :labelcount => 15
34
+ }.merge(args)
35
+
36
+ x = Array.new
37
+ l = Array.new
38
+ y = Array.new
39
+
40
+ yield x, l, y
41
+
42
+ limitlabels(l, args[:labelcount]) if args[:limitlabels]
43
+
44
+ @plot.xrange "[\"#{x.first - 1}\":\"#{x.last + 1}\"]" if args[:setrange]
45
+
46
+ @plot.data << Gnuplot::DataSet.new([x, l, y]) do |ds|
47
+ ds.using = '1:3:(0.5):xtic(2)'
48
+ ds.with = 'boxes fs solid'
49
+ ds.notitle
50
+ end
51
+ end
52
+
53
+ def add_steps(args = {})
54
+ args = {
55
+ :setrange => true,
56
+ :limitlabels => true,
57
+ :labelcount => 15,
58
+ :firstlabel => '""'
59
+ }.merge(args)
60
+
61
+ x = Array.new
62
+ l = Array.new
63
+ y = Array.new
64
+
65
+ yield x, l, y
66
+
67
+ limitlabels(l, args[:labelcount]) if args[:limitlabels]
68
+
69
+ @plot.xrange "[\"#{x.first - 1}\":\"#{x.last + 1}\"]" if args[:setrange]
70
+
71
+ unless args[:firstlabel].nil?
72
+ x.insert(0, x.first - 1)
73
+ l.insert(0, args[:firstlabel])
74
+ y.insert(0, 0)
75
+ end
76
+
77
+ @plot.data << Gnuplot::DataSet.new([x, l, y]) do |ds|
78
+ ds.using = '1:3:xtic(2)'
79
+ ds.with = 'steps'
80
+ ds.notitle
81
+ end
82
+ end
83
+
84
+ private
85
+ def limitlabels(l, maxcount)
86
+ cnt = l.size
87
+ step = (cnt > maxcount) ? (cnt / maxcount + 0.5).round.to_i : 1
88
+
89
+ i = 0
90
+ l.map! do |e|
91
+ unless ((i % step) == 0) || (i == (cnt - 1))
92
+ e = '""'
93
+ end
94
+ i += 1
95
+ e
96
+ end
97
+ l
98
+ end
99
+ end
100
+
101
+ attr_reader :filename
102
+ attr_reader :outdir
103
+ attr_reader :stats
104
+ attr_reader :verbose
105
+
106
+ def initialize(templatedir, filename, outdir, stats, verbose)
107
+ @filename = filename
108
+ @outdir = outdir
109
+ @stats = stats
110
+ @verbose = verbose
111
+
112
+ Dir.glob(File.join(templatedir, 'helpers', '*.rb')).sort.each do |file|
113
+ eval(IO::readlines(file).join(''))
114
+ end
115
+ end
116
+
117
+ def run(lines)
118
+ eval(lines)
119
+ end
120
+
121
+ def defplot(&block)
122
+ plotter = Plotter.new(self)
123
+ plotter.run(&block)
124
+ end
125
+ end
126
+
127
+ def initialize(templatedir, outdir, verbose)
128
+ @templatedir = templatedir
129
+ @outdir = outdir
130
+ @verbose = verbose
131
+ end
132
+
133
+ def name
134
+ 'gnuplot'
135
+ end
136
+
137
+ def handle?(file)
138
+ file =~ /\.plot$/
139
+ end
140
+
141
+ def render(file, stats)
142
+ ifile = File.join(@templatedir, file)
143
+
144
+ lines = IO::readlines(ifile).join('')
145
+
146
+ PlotHelper.new(@templatedir, file, @outdir, stats, @verbose).run(lines)
147
+ end
148
+ end
149
+
150
+ Renderer.register GnuplotRenderer
@@ -0,0 +1,72 @@
1
+ class HamlRenderer
2
+ class HamlHelper
3
+ attr_reader :stats
4
+
5
+ def initialize(templatedir, stats, verbose, engineConfig)
6
+ @templatedir = templatedir
7
+ @stats = stats
8
+ @verbose = verbose
9
+ @layout = nil
10
+ @engineConfig = engineConfig
11
+
12
+ Dir.glob(File.join(templatedir, 'helpers', '*.rb')).sort.each do |file|
13
+ eval(IO::readlines(file).join(''))
14
+ end
15
+ end
16
+
17
+ def partial(name, hash = {})
18
+ name = name.to_s
19
+ puts "rendering partial '#{name}' ..." if @verbose
20
+ hash = hash.to_h unless hash.is_a? Hash
21
+ lines = IO::readlines(File.join(@templatedir, 'partials', "#{name}.haml")).join('')
22
+ engine = Haml::Engine.new(lines, @engineConfig)
23
+ engine.render(self, hash)
24
+ end
25
+
26
+ def layout(name)
27
+ @layout = name.to_s
28
+ end
29
+
30
+ def get_layout
31
+ @layout
32
+ end
33
+ end
34
+
35
+ def initialize(templatedir, outdir, verbose)
36
+ @templatedir = templatedir
37
+ @outdir = outdir
38
+ @verbose = verbose
39
+ @engineConfig = {}
40
+ end
41
+
42
+ def name
43
+ 'haml'
44
+ end
45
+
46
+ def handle?(file)
47
+ file =~ /\.haml$/
48
+ end
49
+
50
+ def render(file, stats)
51
+ ifile = File.join(@templatedir, file)
52
+ ofile = File.join(@outdir, File.basename(file, '.haml') + '.html')
53
+
54
+ lines = IO::readlines(ifile).join('')
55
+
56
+ helper = HamlHelper.new(@templatedir, stats, @verbose, @engineConfig)
57
+
58
+ engine = Haml::Engine.new(lines, @engineConfig)
59
+ lines = engine.render(helper)
60
+
61
+ if !helper.get_layout.nil?
62
+ puts "rendering layout '#{helper.get_layout}' ..." if @verbose
63
+ layout = IO::readlines(File.join(@templatedir, 'layouts', helper.get_layout + '.haml')).join('')
64
+ engine = Haml::Engine.new(layout, @engineConfig)
65
+ lines = engine.render(helper, :content => lines)
66
+ end
67
+
68
+ File.new(ofile, 'w').write(lines)
69
+ end
70
+ end
71
+
72
+ Renderer.register HamlRenderer
@@ -0,0 +1,33 @@
1
+ class SassRenderer
2
+ def initialize(templatedir, outdir, verbose)
3
+ @templatedir = templatedir
4
+ @outdir = outdir
5
+ @verbose = verbose
6
+ end
7
+
8
+ def name
9
+ 'sass/compass'
10
+ end
11
+
12
+ def handle?(file)
13
+ (file =~ /\.sass$/) || (file =~ /\.scss$/)
14
+ end
15
+
16
+ def render(file, stats)
17
+ scss = file =~ /\.scss$/
18
+
19
+ ifile = File.join(@templatedir, file)
20
+ ofile = File.join(@outdir, File.basename(file, scss ? '.scss' : '.sass') + '.css')
21
+
22
+ lines = IO::readlines(ifile).join('')
23
+
24
+ options = Compass.sass_engine_options
25
+ options[:syntax] = scss ? :scss : :sass
26
+ engine = Sass::Engine.new(lines, options)
27
+ lines = engine.render
28
+
29
+ File.new(ofile, 'w').write(lines)
30
+ end
31
+ end
32
+
33
+ Renderer.register SassRenderer
@@ -0,0 +1,119 @@
1
+ class StatGen
2
+ attr_accessor :verbose
3
+ attr_accessor :debug
4
+ attr_accessor :quiet
5
+ attr_accessor :future
6
+ attr_accessor :maxage
7
+ attr_accessor :commitcache
8
+ attr_accessor :include_mail
9
+ attr_reader :repos
10
+
11
+ attr_reader :num_commits
12
+
13
+ attr_reader :general_stats
14
+ attr_reader :author_stats
15
+ attr_reader :year_stats
16
+ attr_reader :month_stats
17
+ attr_reader :yearmonth_stats
18
+ attr_reader :date_stats
19
+ attr_reader :hour_stats
20
+ attr_reader :wday_stats
21
+ attr_reader :lastweeks_stats
22
+
23
+ attr_reader :file_stats
24
+ attr_reader :filetype_stats
25
+
26
+ def initialize
27
+ @repos = Array.new
28
+ @repostate = Hash.new
29
+
30
+ @verbose = false
31
+ @debug = false
32
+ @quiet = false
33
+ @future = true
34
+ @maxage = 0
35
+ @commitcache = nil
36
+ @include_mail = false
37
+
38
+ @num_commits = 0
39
+ @general_stats = CommitStats.new
40
+ @author_stats = AuthorsCommitStats.new
41
+ @year_stats = YearCommitStats.new
42
+ @month_stats = MonthCommitStats.new
43
+ @yearmonth_stats = YearMonthCommitStats.new
44
+ @date_stats = DateCommitStats.new
45
+ @hour_stats = HourCommitStats.new
46
+ @wday_stats = DayOfWeekCommitStats.new
47
+ @lastweeks_stats = LastWeeksCommitStats.new
48
+
49
+ @file_stats = FileStats.new
50
+ @filetype_stats = FileTypeFileStats.new
51
+ end
52
+
53
+ def num_authors
54
+ @author_stats.size
55
+ end
56
+
57
+ def clear_repos
58
+ @repos = Array.new
59
+ end
60
+
61
+ def check_repostate
62
+ @repostate.keys.each do |name|
63
+ return false if @repos.find { |x| x.name == name }.nil?
64
+ end
65
+ true
66
+ end
67
+
68
+ def add(name, directory, ref = 'HEAD')
69
+ cache = nil
70
+ unless @commitcache.nil?
71
+ cache = File.join(@commitcache, ".commitcache.#{name.tr('^a-zA-Z0-9_', '_')}")
72
+ end
73
+ @repos << Git.new(name, directory, ref, @debug, cache)
74
+ end
75
+
76
+ def <<(value)
77
+ add(value[0], value[1], value[2])
78
+ end
79
+
80
+ def calc
81
+ # reset because of caching for now
82
+ @file_stats = FileStats.new
83
+ @filetype_stats = FileTypeFileStats.new
84
+
85
+ @repos.each do |repo|
86
+ @repostate[repo.name] ||= {
87
+ :last => nil
88
+ }
89
+
90
+ puts " repository '#{repo.name}' ..." unless @quiet
91
+
92
+ repo.get_commits(@repostate[repo.name][:last]) do |commit|
93
+ next if !@future && (commit[:time] > Time.now)
94
+ next if (@maxage > 0) && ((Time.now - commit[:time]) > @maxage)
95
+
96
+ puts " commit #{@num_commits} ..." if @verbose && ((@num_commits % 100) == 0)
97
+
98
+ @num_commits += 1
99
+ @general_stats.update(commit)
100
+ @author_stats.update(commit)
101
+ @year_stats.update(commit)
102
+ @month_stats.update(commit)
103
+ @yearmonth_stats.update(commit)
104
+ @date_stats.update(commit)
105
+ @hour_stats.update(commit)
106
+ @wday_stats.update(commit)
107
+ @lastweeks_stats.update(commit)
108
+
109
+ @repostate[repo.name][:last] = commit[:hash]
110
+ end
111
+
112
+ repo.get_files do |file|
113
+ @file_stats.update(file)
114
+ @filetype_stats.update(file)
115
+ end
116
+ end
117
+ end
118
+ end
119
+
@@ -0,0 +1,12 @@
1
+ module StatsHash
2
+ def method_missing(method, *args, &block)
3
+ @hash.send(method, *args, &block)
4
+ end
5
+
6
+ def each_sorted
7
+ @hash.keys.sort.each do |key|
8
+ yield key, @hash[key]
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,63 @@
1
+ class CommitAccumulator
2
+ attr_reader :commits
3
+ attr_reader :files_added
4
+ attr_reader :files_deleted
5
+ attr_reader :lines_added
6
+ attr_reader :lines_deleted
7
+ attr_reader :first_commit
8
+ attr_reader :last_commit
9
+
10
+ def files
11
+ @files_added - @files_deleted
12
+ end
13
+
14
+ def lines
15
+ @lines_added - @lines_deleted
16
+ end
17
+
18
+ def initialize
19
+ @commits = 0
20
+ @files_added = 0
21
+ @files_deleted = 0
22
+ @lines_added = 0
23
+ @lines_deleted = 0
24
+ @first_commit = nil
25
+ @last_commit = nil
26
+ end
27
+
28
+ def update(commit)
29
+ @commits += 1
30
+ @files_added += commit[:files_added]
31
+ @files_deleted += commit[:files_deleted]
32
+ @lines_added += commit[:lines_added]
33
+ @lines_deleted += commit[:lines_deleted]
34
+
35
+ @first_commit ||= commit[:time]
36
+ @last_commit ||= commit[:time]
37
+
38
+ @first_commit = commit[:time] if commit[:time] < @first_commit
39
+ @last_commit = commit[:time] if commit[:time] > @last_commit
40
+ end
41
+ end
42
+
43
+ class CommitStats < CommitAccumulator
44
+ def initialize
45
+ super
46
+ @days = {}
47
+ end
48
+
49
+ def days
50
+ @days.size
51
+ end
52
+
53
+ def daystats
54
+ @days
55
+ end
56
+
57
+ def update(commit)
58
+ super(commit)
59
+ day = commit[:time].to_date
60
+ @days[day] ||= CommitAccumulator.new
61
+ @days[day].update(commit)
62
+ end
63
+ end