gitalytics 1.1.1 → 1.2.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.
- checksums.yaml +4 -4
- data/assets/gitalytics.html.erb +96 -28
- data/bin/gitalytics +10 -2
- data/lib/gitalytics.rb +26 -116
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfee4701c631f00b32ae513f27dd761e78bc90c9
|
4
|
+
data.tar.gz: 18dfec6a19254bd61de7ecb7a65b34b824f610f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22ab60c1cc856e08152c8893170c567b82831ff09910c7d4a7e7107cc06576042651ef0001a71a7533da5557b4d31a0ab3d515fe897fb07531e32b62ce1d45b1
|
7
|
+
data.tar.gz: f24764d467fd4e7df3f06a37804f3185d2e07157241e38764ff70c4e013ccef02c81377c1459998d1f75e1daf3f729d4c560d8395b8e26e8eff50f0a1c24b9b0
|
data/assets/gitalytics.html.erb
CHANGED
@@ -79,6 +79,18 @@
|
|
79
79
|
/*-webkit-font-smoothing:antialiased;*/
|
80
80
|
}
|
81
81
|
|
82
|
+
.dashboard-box {
|
83
|
+
background-color: #f3f3f3;
|
84
|
+
border-radius: 10px;
|
85
|
+
margin-bottom: 30px;
|
86
|
+
padding: 20px;
|
87
|
+
text-align: center;
|
88
|
+
}
|
89
|
+
|
90
|
+
.dashboard-box .glyphicon {
|
91
|
+
font-size: 48px;
|
92
|
+
}
|
93
|
+
|
82
94
|
</style>
|
83
95
|
</head>
|
84
96
|
|
@@ -101,9 +113,9 @@
|
|
101
113
|
</div>
|
102
114
|
<div class="collapse navbar-collapse">
|
103
115
|
<ul class="nav navbar-nav">
|
104
|
-
<li class="active"><a href="#">
|
105
|
-
|
106
|
-
<li><a href="#contact">Contact</a></li> -->
|
116
|
+
<li class="active"><a href="#dashboard" data-toggle="tab">Dashboard</a></li>
|
117
|
+
<li><a href="#authors" data-toggle="tab">Authors</a></li>
|
118
|
+
<!-- <li><a href="#contact">Contact</a></li> -->
|
107
119
|
</ul>
|
108
120
|
</div><!--/.nav-collapse -->
|
109
121
|
</div>
|
@@ -111,28 +123,84 @@
|
|
111
123
|
|
112
124
|
<!-- Begin page content -->
|
113
125
|
<div class="container">
|
114
|
-
<div class="
|
115
|
-
<
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
126
|
+
<div class="tab-content">
|
127
|
+
<div class="tab-pane active" id="dashboard">
|
128
|
+
<div class="page-header">
|
129
|
+
<h1>Dashboard</h1>
|
130
|
+
<p class="lead">A quick overview of the activity on your git repository.</p>
|
131
|
+
</div>
|
132
|
+
<div class="row">
|
133
|
+
<div class="col-md-4">
|
134
|
+
<div class="dashboard-box">
|
135
|
+
<span class="glyphicon glyphicon-user"></span><br>
|
136
|
+
<%= @users.count %>
|
137
|
+
authors
|
138
|
+
</div>
|
139
|
+
</div>
|
140
|
+
<div class="col-md-4">
|
141
|
+
<div class="dashboard-box">
|
142
|
+
<span class="glyphicon glyphicon-list"></span><br>
|
143
|
+
<%= @commits.count %>
|
144
|
+
commits
|
145
|
+
</div>
|
146
|
+
</div>
|
147
|
+
<div class="col-md-4">
|
148
|
+
<div class="dashboard-box">
|
149
|
+
<span class="glyphicon glyphicon-file"></span><br>
|
150
|
+
<%= @commits.map{ |c| c.files_committed }.flatten.uniq.compact.count %>
|
151
|
+
files committed
|
152
|
+
</div>
|
153
|
+
</div>
|
154
|
+
</div>
|
155
|
+
<div class="row">
|
156
|
+
<div class="col-md-4">
|
157
|
+
<div class="dashboard-box">
|
158
|
+
<span class="glyphicon glyphicon-plus"></span><br>
|
159
|
+
<%= @commits.map{ |c| c.insertions }.inject(0){ |total, current| total + current } %>
|
160
|
+
insertions
|
161
|
+
</div>
|
162
|
+
</div>
|
163
|
+
<div class="col-md-4">
|
164
|
+
<div class="dashboard-box">
|
165
|
+
<span class="glyphicon glyphicon-minus"></span><br>
|
166
|
+
<%= @commits.map{ |c| c.deletions }.inject(0){ |total, current| total + current } %>
|
167
|
+
deletions
|
168
|
+
</div>
|
169
|
+
</div>
|
170
|
+
<div class="col-md-4">
|
171
|
+
<div class="dashboard-box">
|
172
|
+
<span class="glyphicon glyphicon-calendar"></span><br>
|
173
|
+
<%= (@commits.max_by(&:date).date - @commits.min_by(&:date).date).to_i + 1 %>
|
174
|
+
days
|
175
|
+
</div>
|
176
|
+
</div>
|
177
|
+
</div>
|
127
178
|
</div>
|
128
|
-
<div class="
|
129
|
-
<
|
130
|
-
|
179
|
+
<div class="tab-pane" id="authors">
|
180
|
+
<div class="page-header">
|
181
|
+
<h1>Authors</h1>
|
182
|
+
<p class="lead">Compare the quantity of commits made by each of your repository contributors.<br/> Check who is writing most of the code of your project and maybe give him/her a prize! ;)</p>
|
183
|
+
<% @users.each do |user| %>
|
184
|
+
<a href="#" title="<%= user.name %> <small><%= user.email %></small>" class="user-avatar" data-content="<%= user.summary %>">
|
185
|
+
<img class="dp img-circle" src="<%= "http://www.gravatar.com/avatar/#{user.gravatar}" %>" style="width: 100px; height:100px; border-color:<%= user.rgba %>"></a>
|
186
|
+
<% end %>
|
187
|
+
</div>
|
188
|
+
|
189
|
+
<div class="row">
|
190
|
+
<div class="col-md-6">
|
191
|
+
<h2>Commits count per user</h2>
|
192
|
+
<canvas id="usersPieChart" width="550" height="400"></canvas>
|
193
|
+
</div>
|
194
|
+
<div class="col-md-6">
|
195
|
+
<h2>Commits per weekday per user</h2>
|
196
|
+
<canvas id="usersRadarChart" width="550" height="400"></canvas>
|
197
|
+
</div>
|
198
|
+
</div>
|
199
|
+
<hr>
|
200
|
+
<h2>Insertions vs. Deletions</h2>
|
201
|
+
<canvas id="usersBarChart" width="1100" height="400"></canvas>
|
131
202
|
</div>
|
132
203
|
</div>
|
133
|
-
<hr>
|
134
|
-
<h2>Insertions vs. Deletions</h2>
|
135
|
-
<canvas id="usersBarChart" width="1100" height="400"></canvas>
|
136
204
|
</div>
|
137
205
|
</div>
|
138
206
|
|
@@ -155,21 +223,21 @@
|
|
155
223
|
});
|
156
224
|
|
157
225
|
// Data for Users Pie Chart
|
158
|
-
var data = [<%= @
|
226
|
+
var data = [<%= @users.map{|d| "{ value: #{d.commits.count}, color: '#{d.rgba}' }" }.join(',') %>];
|
159
227
|
var ctx = document.getElementById("usersPieChart").getContext("2d");
|
160
228
|
var usersPieChart = new Chart(ctx).Pie(data);
|
161
229
|
|
162
230
|
// Data for Insertions vs. Deletions Bar Chart
|
163
231
|
var data = {
|
164
|
-
labels: [<%= @
|
232
|
+
labels: [<%= @users.map{|d| "'#{d.name}'" }.join(',') %>],
|
165
233
|
datasets: [
|
166
234
|
{
|
167
|
-
data: [<%= @
|
235
|
+
data: [<%= @users.map{|d| "#{d.total_insertions}" }.join(',') %>],
|
168
236
|
fillColor: 'rgba(196, 234, 44, 0.5)',
|
169
237
|
strokeColor: 'rgba(196, 234, 44, 1)'
|
170
238
|
},
|
171
239
|
{
|
172
|
-
data: [<%= @
|
240
|
+
data: [<%= @users.map{|d| "#{d.total_deletions}" }.join(',') %>],
|
173
241
|
fillColor: 'rgba(234, 80, 44, 0.5)',
|
174
242
|
strokeColor: 'rgba(234, 80, 44, 1)'
|
175
243
|
}
|
@@ -182,14 +250,14 @@
|
|
182
250
|
var data = {
|
183
251
|
labels: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
|
184
252
|
datasets: [
|
185
|
-
<% @
|
253
|
+
<% @users.each_with_index do |user, i| %>
|
186
254
|
{
|
187
255
|
fillColor : "<%= user.rgba('0.5') %>",
|
188
256
|
strokeColor : "<%= user.rgba %>",
|
189
257
|
pointColor : "<%= user.rgba %>",
|
190
258
|
pointStrokeColor : "#fff",
|
191
259
|
data : [<%= user.weekday_commits.join(', ') %>]
|
192
|
-
}<%= ',' unless i + 1 == @
|
260
|
+
}<%= ',' unless i + 1 == @users.count %>
|
193
261
|
<% end %>
|
194
262
|
]
|
195
263
|
};
|
data/bin/gitalytics
CHANGED
@@ -2,13 +2,15 @@
|
|
2
2
|
# encoding: UTF-8
|
3
3
|
|
4
4
|
Encoding.default_external = Encoding::UTF_8
|
5
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
|
5
6
|
|
6
7
|
require 'optparse'
|
7
|
-
require
|
8
|
+
require 'gitalytics'
|
8
9
|
|
9
10
|
options = {
|
10
11
|
format: 'cli'
|
11
12
|
}
|
13
|
+
|
12
14
|
OptionParser.new do |opts|
|
13
15
|
opts.banner = "Usage: gitalytics [options]"
|
14
16
|
|
@@ -17,8 +19,14 @@ OptionParser.new do |opts|
|
|
17
19
|
exit
|
18
20
|
end
|
19
21
|
|
20
|
-
opts.on("-h", "--html", "Outputs html report") do |v|
|
22
|
+
opts.on("-h", "--html", "Outputs html report and displays it in default web browser") do |v|
|
23
|
+
options[:format] = 'html'
|
24
|
+
options[:browser] = true
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-n", "--no-browser", "Do not open html report in browser") do |v|
|
21
28
|
options[:format] = 'html'
|
29
|
+
options[:browser] = false
|
22
30
|
end
|
23
31
|
end.parse!
|
24
32
|
|
data/lib/gitalytics.rb
CHANGED
@@ -4,144 +4,54 @@ require 'date'
|
|
4
4
|
require 'erb'
|
5
5
|
require 'digest/md5'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
require 'gitlog'
|
8
|
+
require 'user'
|
9
|
+
require 'commit'
|
10
10
|
|
11
|
-
|
11
|
+
class Gitalytics
|
12
12
|
|
13
|
-
|
14
|
-
@data = { commits: [], users: [] }
|
15
|
-
end
|
13
|
+
VERSION = '1.2.0'
|
16
14
|
|
17
15
|
def analyze(options)
|
18
|
-
parse_git_log
|
16
|
+
data = GitLog.parse_git_log
|
17
|
+
|
19
18
|
case options[:format]
|
20
19
|
when 'html'
|
21
|
-
output_html_report
|
20
|
+
output_html_report(data, options[:browser])
|
22
21
|
else
|
23
|
-
output_cli_report
|
22
|
+
output_cli_report(data[:users])
|
24
23
|
end
|
25
24
|
end
|
26
25
|
|
27
26
|
private
|
28
|
-
def parse_git_log
|
29
|
-
result = `git log --stat --format=`
|
30
|
-
|
31
|
-
result.each_line do |line|
|
32
|
-
line.encode!('UTF-8', 'UTF8-MAC') if defined?(Encoding::UTF8_MAC)
|
33
27
|
|
34
|
-
|
35
|
-
|
36
|
-
elsif match = line.match(/^Author: ([[:alpha:] ]*) <(.*)>$/)
|
37
|
-
user = get_user(match[1], match[2])
|
38
|
-
@data[:commits].last.author = user
|
39
|
-
user.commits << @data[:commits].last
|
40
|
-
elsif match = line.match(/^Date: (.*)$/)
|
41
|
-
@data[:commits].last.date = Date.parse(match[1])
|
42
|
-
elsif match = line.match(/^ (.*)$/)
|
43
|
-
@data[:commits].last.subject = match[1]
|
44
|
-
elsif match = line.match(/^ ([^ ]+)[ ]+\|[ ]+([\d]+) ([\+]*)([-]*)$/)
|
45
|
-
@data[:commits].last.summary << { filename: match[1], changes: match[2], insertions: match[3], deletions: match[4] }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
def output_cli_report
|
52
|
-
@data[:users].each do |user|
|
28
|
+
def output_cli_report(users)
|
29
|
+
users.each do |user|
|
53
30
|
puts user.summary
|
54
31
|
end
|
55
32
|
end
|
56
33
|
|
57
|
-
def output_html_report
|
34
|
+
def output_html_report(data, open_in_browser)
|
58
35
|
template_file = File.read(File.join(File.dirname(__FILE__), "..", "assets", "gitalytics.html.erb"))
|
59
36
|
erb = ERB.new(template_file)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
return u if u.email == email
|
37
|
+
output_file = "gitalytics_result.html"
|
38
|
+
File.open(output_file, 'w+') do |file|
|
39
|
+
@users = data[:users]
|
40
|
+
@commits = data[:commits]
|
41
|
+
file.write(erb.result(binding))
|
66
42
|
end
|
67
|
-
|
68
|
-
new_user
|
69
|
-
end
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
class User
|
74
|
-
|
75
|
-
attr_accessor :name, :email, :commits, :colors
|
76
|
-
|
77
|
-
def initialize(name, email)
|
78
|
-
self.name = name
|
79
|
-
self.email = email
|
80
|
-
self.commits = []
|
81
|
-
self.colors = [rand(255), rand(255), rand(255)].join(', ')
|
82
|
-
end
|
83
|
-
|
84
|
-
def gravatar
|
85
|
-
Digest::MD5.hexdigest(email)
|
86
|
-
end
|
87
|
-
|
88
|
-
def first_commit
|
89
|
-
commits.min_by(&:date)
|
90
|
-
end
|
91
|
-
|
92
|
-
def last_commit
|
93
|
-
commits.max_by(&:date)
|
94
|
-
end
|
95
|
-
|
96
|
-
def commits_period
|
97
|
-
(last_commit.date - first_commit.date).to_i + 1
|
98
|
-
end
|
99
|
-
|
100
|
-
def working_days
|
101
|
-
commits.map(&:date).uniq.count
|
102
|
-
end
|
103
|
-
|
104
|
-
def total_insertions
|
105
|
-
commits.map(&:insertions).inject(0) { |total, current| total + current }
|
106
|
-
end
|
107
|
-
|
108
|
-
def total_deletions
|
109
|
-
commits.map(&:deletions).inject(0) { |total, current| total + current }
|
43
|
+
open_report_in_browser(output_file) if open_in_browser
|
110
44
|
end
|
111
45
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
def weekday_commits
|
121
|
-
days = Array.new(7) {0}
|
122
|
-
commits.each do |c|
|
123
|
-
days[c.date.wday] += 1
|
46
|
+
def open_report_in_browser(filename)
|
47
|
+
host = RbConfig::CONFIG['host_os']
|
48
|
+
if host =~ /mswin|mingw|cygwin/
|
49
|
+
system "start #{filename}"
|
50
|
+
elsif host =~ /darwin/
|
51
|
+
system "open #{filename}"
|
52
|
+
elsif host =~ /linux|bsd/
|
53
|
+
system "xdg-open #{filename}"
|
124
54
|
end
|
125
|
-
days
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
129
|
-
|
130
|
-
class Commit
|
131
|
-
|
132
|
-
attr_accessor :hash, :author, :date, :subject, :summary
|
133
|
-
|
134
|
-
def initialize(hash)
|
135
|
-
self.hash = hash
|
136
|
-
self.summary = []
|
137
|
-
end
|
138
|
-
|
139
|
-
def insertions
|
140
|
-
summary.map{ |s| s[:insertions].length }.inject(0){ |total, current| total + current }
|
141
|
-
end
|
142
|
-
|
143
|
-
def deletions
|
144
|
-
summary.map{ |s| s[:deletions].length }.inject(0){ |total, current| total + current }
|
145
55
|
end
|
146
56
|
|
147
57
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitalytics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gonzalo Robaina
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Get usefull analytics from your git log
|
14
14
|
email: gonzalor@gmail.com
|