gitalytics 1.2.2 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 42ce0238a2282e8e07b38afc990e971c93083d3f
4
- data.tar.gz: 4f3eb1308a8ed7c6b32a11987bfaa5eb022b8c40
3
+ metadata.gz: a3f105e2f5f0c549981b100e5ebfa66d22c86fef
4
+ data.tar.gz: 432428efcdfea8e4dc3c3eec59ece71a39092692
5
5
  SHA512:
6
- metadata.gz: 071ad7c30c498c430540f53b3478e76b7f069e71768e30f70f6eb86b5353110c5d542b49f37a36486a01e203501b7e16cd44b97496a45a28f5d60807d0764d79
7
- data.tar.gz: 791dc2e44997462d29fd24c0ac40165eaff230cb59d5c108bb8f1910b8b40bb2a4a83db52e3b0afd950bd5da128d17d919651af6cabcc09647196095a82f32e4
6
+ metadata.gz: 90fcb57c95019c47583cb0f6c9cc909d12addb5db13e430e118dc8f5ea2028adde466c9ac16ba7b862344ca93eb003a97d6ce5869edc33810a68d9269572f935
7
+ data.tar.gz: ffb1ecd259992afa952df44a83d5c01f2492e3c855084d85a27068cd0366390f63ab622383f701ee0b08e7de9a06aa92412e55af3af7ab5cc21ae9bbb549af84
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.5
6
+ - 2.2.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ ## Gitalytics Changelog
2
+
3
+ #### v.1.3.0
4
+ * Changed CLI output text.
5
+ * Re-structure of gem's source code.
6
+ * Authors are now grouped by name.
7
+ * Added CLI option -e to group authors by email (old behavior).
8
+ * Updated Chart.js version.
9
+ * Updated Bootstrap version.
10
+ * Updated HTML report design.
11
+
12
+ #### v.1.2.1
13
+ * Fixed bug that prevented gitalytics to initialize correctly. (Thanks @willyschwindt)
14
+
15
+ #### v.1.2.0
16
+ * Git log parser totally refactored. (Thanks @grilix)
17
+ * Gitalytics now open html report in browser automatically. (Thanks @surendrans)
18
+ * Moved classes and modules to external files.
19
+ * Added CLI option -n that prevents report to load automatically in web browser.
20
+ * Dashboard added with some quick stats for your git repository.
21
+
22
+ #### v1.1.1
23
+ * Add Changelog :)
24
+ * Update Readme.
25
+ * Fix bug which preventing authors with special characters to be shown on the report. (thanks @afrojas)
26
+ * Fix bug which may result in a broken report if the user uses a default format for git log. (thanks @dgiunta)
27
+
28
+ #### v1.1.0
29
+ * Add Insertions vs. Deletions graph for Users.
30
+ * Add weekday commits count graph for Users.
31
+ * Change the way it shows the authors information.
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'coveralls', require: false
6
+
7
+ group :test do
8
+ gem 'rake'
9
+ end
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ ## Gitalytics
2
+
3
+ Get commits statistics for your git repository
4
+ Based on gist by Tobin Harris: https://gist.github.com/tobinharris/396634
5
+
6
+ [![Code Climate](https://codeclimate.com/github/pepito2k/gitalytics.png)](https://codeclimate.com/github/pepito2k/gitalytics)
7
+ [![Gem Version](https://badge.fury.io/rb/gitalytics.png)](http://badge.fury.io/rb/gitalytics)
8
+ [![Dependency Status](https://gemnasium.com/pepito2k/gitalytics.png)](https://gemnasium.com/pepito2k/gitalytics)
9
+ [![Build Status](https://travis-ci.org/pepito2k/gitalytics.png)](https://travis-ci.org/pepito2k/gitalytics)
10
+ [![Coverage Status](https://coveralls.io/repos/pepito2k/gitalytics/badge.png?branch=master)](https://coveralls.io/r/pepito2k/gitalytics?branch=master)
11
+
12
+ ![Gitalytics Screenshot](http://photos.gonzalo.robaina.me/gitalytics_screen.png "Gitalytics Screenshot")
13
+
14
+ ## Installation
15
+
16
+ ### Manual installation
17
+
18
+ gem install gitalytics
19
+
20
+ ### Using bundler
21
+ Add gitalytics to your Gemfile
22
+
23
+ gem "gitalytics"
24
+
25
+ Install the gem
26
+
27
+ bundle install
28
+
29
+ ## Usage
30
+ Open a terminal window pointing to your git repository and just run the gitalytics command:
31
+
32
+ $ gitalytics
33
+
34
+ If you'd like to get a fancy html report like the one in the screenshot above, you will need to run the following command:
35
+
36
+ $ gitalytics -h
37
+
38
+ If you want to have the html report but don't want it to be opened right away just write:
39
+
40
+ $ gitalytics -n
41
+
42
+ Note that gitalytics groups authors by name, you can change that and group by email using:
43
+
44
+ $ gitalytics -e
45
+
46
+ ## License
47
+ Copyright (c) 2016 Gonzalo Robaina
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining a copy
50
+ of this software and associated documentation files (the "Software"), to deal
51
+ in the Software without restriction, including without limitation the rights
52
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
53
+ copies of the Software, and to permit persons to whom the Software is
54
+ furnished to do so, subject to the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be included in
57
+ all copies or substantial portions of the Software.
58
+
59
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
62
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
63
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
64
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
65
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'lib'
5
+ t.pattern = 'test/**/*test_*.rb'
6
+ end
7
+
8
+ task default: :test
@@ -4,13 +4,14 @@
4
4
  <meta charset="utf-8">
5
5
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <meta name="description" content="">
8
- <meta name="author" content="">
7
+ <meta name="description" content="Gitalytics Report">
8
+ <meta name="author" content="Gonzalo Robaina">
9
9
 
10
10
  <title>Gitalytics report</title>
11
11
 
12
12
  <!-- Bootstrap core CSS -->
13
- <link href="http://netdna.bootstrapcdn.com/bootswatch/3.0.3/cerulean/bootstrap.min.css" rel="stylesheet">
13
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
14
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
14
15
 
15
16
  <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
16
17
  <!--[if lt IE 9]>
@@ -40,10 +41,115 @@
40
41
 
41
42
  /* Set the fixed height of the footer here */
42
43
  #footer {
43
- height: 60px;
44
44
  background-color: #f5f5f5;
45
+ height: 60px;
46
+ margin-top: 60px;
45
47
  }
46
48
 
49
+ /* Twitter Profile styles
50
+ -------------------------------------------------- */
51
+ .twPc-div {
52
+ background: #fff none repeat scroll 0 0;
53
+ border: 1px solid #e1e8ed;
54
+ border-radius: 6px;
55
+ height: 150px;
56
+ max-width: 340px;
57
+ margin-bottom: 40px;
58
+ }
59
+ .twPc-bg {
60
+ border-bottom: 1px solid #e1e8ed;
61
+ border-radius: 4px 4px 0 0;
62
+ height: 40px;
63
+ width: 100%;
64
+ }
65
+ .twPc-block {
66
+ display: block !important;
67
+ }
68
+ .twPc-button {
69
+ margin: -35px -10px 0;
70
+ text-align: right;
71
+ width: 100%;
72
+ }
73
+ .twPc-avatarLink {
74
+ background-color: #fff;
75
+ border-radius: 6px;
76
+ display: inline-block !important;
77
+ float: left;
78
+ margin: -30px 5px 0 8px;
79
+ max-width: 100%;
80
+ padding: 1px;
81
+ vertical-align: bottom;
82
+ }
83
+ .twPc-avatarImg {
84
+ border: 2px solid #fff;
85
+ border-radius: 7px;
86
+ box-sizing: border-box;
87
+ color: #fff;
88
+ height: 72px;
89
+ width: 72px;
90
+ }
91
+ .twPc-divUser {
92
+ margin: 5px 0 0;
93
+ }
94
+ .twPc-divName {
95
+ font-size: 18px;
96
+ font-weight: 700;
97
+ line-height: 21px;
98
+ white-space: nowrap;
99
+ overflow: hidden;
100
+ text-overflow: ellipsis
101
+ }
102
+ .twPc-divName a {
103
+ color: inherit !important;
104
+ }
105
+ .twPc-divStats {
106
+ margin-left: 11px;
107
+ padding: 10px 0;
108
+ }
109
+ .twPc-Arrange {
110
+ box-sizing: border-box;
111
+ display: table;
112
+ margin: 0;
113
+ min-width: 100%;
114
+ padding: 0;
115
+ table-layout: auto;
116
+ }
117
+ ul.twPc-Arrange {
118
+ list-style: outside none none;
119
+ margin: 0;
120
+ padding: 0;
121
+ }
122
+ .twPc-ArrangeSizeFit {
123
+ display: table-cell;
124
+ padding: 0;
125
+ vertical-align: top;
126
+ }
127
+ .twPc-ArrangeSizeFit a:hover {
128
+ text-decoration: none;
129
+ }
130
+ .twPc-StatValue {
131
+ display: block;
132
+ font-size: 18px;
133
+ font-weight: 500;
134
+ transition: color 0.15s ease-in-out 0s;
135
+ }
136
+ .twPc-StatValue.commits {
137
+ color: #a650c0;
138
+ }
139
+ .twPc-StatValue.insertions {
140
+ color: #539734;
141
+ }
142
+ .twPc-StatValue.deletions {
143
+ color: #c03c24;
144
+ }
145
+ .twPc-StatLabel {
146
+ color: #8899a6;
147
+ font-size: 10px;
148
+ letter-spacing: 0.02em;
149
+ overflow: hidden;
150
+ text-transform: uppercase;
151
+ transition: color 0.15s ease-in-out 0s;
152
+ }
47
153
 
48
154
  /* Custom page CSS
49
155
  -------------------------------------------------- */
@@ -96,6 +202,14 @@
96
202
  font-size: 48px;
97
203
  }
98
204
 
205
+ #authors li {
206
+ list-style: none;
207
+ }
208
+
209
+ .usersColorLegend {
210
+ vertical-align: middle;
211
+ }
212
+
99
213
  </style>
100
214
  </head>
101
215
 
@@ -120,6 +234,8 @@
120
234
  <ul class="nav navbar-nav">
121
235
  <li class="active"><a href="#dashboard" data-toggle="tab">Dashboard</a></li>
122
236
  <li><a href="#authors" data-toggle="tab">Authors</a></li>
237
+ <li><a href="#commits" data-toggle="tab">Commits</a></li>
238
+ <li><a href="#dates" data-toggle="tab">Dates</a></li>
123
239
  <!-- <li><a href="#contact">Contact</a></li> -->
124
240
  </ul>
125
241
  </div><!--/.nav-collapse -->
@@ -129,6 +245,7 @@
129
245
  <!-- Begin page content -->
130
246
  <div class="container">
131
247
  <div class="tab-content">
248
+ <!-- Start Dashboard -->
132
249
  <div class="tab-pane active" id="dashboard">
133
250
  <div class="page-header">
134
251
  <h1>Dashboard</h1>
@@ -183,30 +300,136 @@
183
300
  </div>
184
301
  </div>
185
302
  </div>
303
+ <!-- End Dashboard -->
304
+ <!-- Start Authors -->
186
305
  <div class="tab-pane" id="authors">
187
306
  <div class="page-header">
188
307
  <h1>Authors</h1>
189
308
  <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>
309
+ </div>
310
+ <div class="row">
311
+ <p>So far, <%= @users.size %> contributors commited something to your project.</p>
190
312
  <% @users.each do |user| %>
191
- <a href="#" title="<%= user.name %> <small><%= user.email %></small>" class="user-avatar" data-content="<%= user.summary %>">
192
- <img class="dp img-circle" src="<%= "http://www.gravatar.com/avatar/#{user.gravatar}?d=mm" %>" style="width: 100px; height:100px; border-color:<%= user.rgba %>"></a>
313
+ <div class="col-md-4">
314
+ <!-- Twitter Profile start -->
315
+ <div class="twPc-div">
316
+ <a class="twPc-bg twPc-block" style="background-color: <%= user.rgba %>"></a>
317
+ <div>
318
+
319
+ <a title="<%= user.name %>" href="#" class="twPc-avatarLink">
320
+ <img alt="<%= user.name %>" src="<%= "http://www.gravatar.com/avatar/#{user.gravatar}?d=mm" %>" class="twPc-avatarImg">
321
+ </a>
322
+
323
+ <div class="twPc-divUser">
324
+ <div class="twPc-divName">
325
+ <a href="#"><%= user.name %></a>
326
+ </div>
327
+ <span>
328
+ <a href="mailto:<%= user.email %>"><span><%= user.email %></span></a>
329
+ </span>
330
+ </div>
331
+
332
+ <div class="twPc-divStats">
333
+ <ul class="twPc-Arrange">
334
+ <li class="twPc-ArrangeSizeFit">
335
+ <span class="twPc-StatLabel twPc-block">Commits</span>
336
+ <span class="twPc-StatValue commits"><%= user.commits.size %></span>
337
+ </li>
338
+ <li class="twPc-ArrangeSizeFit">
339
+ <span class="twPc-StatLabel twPc-block">Inserts</span>
340
+ <span class="twPc-StatValue insertions"><%= user.total_insertions %></span>
341
+ </li>
342
+ <li class="twPc-ArrangeSizeFit">
343
+ <span class="twPc-StatLabel twPc-block">Deletes</span>
344
+ <span class="twPc-StatValue deletions"><%= user.total_deletions %></span>
345
+ </li>
346
+ </ul>
347
+ </div>
348
+ </div>
349
+ </div>
350
+ <!-- Twitter Profile end -->
351
+ </div>
193
352
  <% end %>
194
353
  </div>
195
354
 
196
355
  <div class="row">
356
+ <h2>Commits count per user</h2>
197
357
  <div class="col-md-6">
198
- <h2>Commits count per user</h2>
199
358
  <canvas id="usersPieChart" width="550" height="400"></canvas>
200
359
  </div>
201
360
  <div class="col-md-6">
202
- <h2>Commits per weekday per user</h2>
203
- <canvas id="usersRadarChart" width="550" height="400"></canvas>
361
+ <ul>
362
+ <% @users.each do |u| %>
363
+ <li>
364
+ <canvas class="usersColorLegend" width="30" height="15" style="background-color: <%= u.rgba %>"></canvas>
365
+ <%= u.name %>
366
+ </li>
367
+ <% end %>
368
+ <ul>
204
369
  </div>
205
370
  </div>
206
- <hr>
371
+ <!-- <hr>
207
372
  <h2>Insertions vs. Deletions</h2>
208
- <canvas id="usersBarChart" width="1100" height="400"></canvas>
373
+ <canvas id="usersBarChart" width="1100" height="400"></canvas> -->
374
+ </div>
375
+ <!-- End Authors -->
376
+ <!-- Start Commits -->
377
+ <div class="tab-pane" id="commits">
378
+ <div class="page-header">
379
+ <h1>Commits</h1>
380
+ <p class="lead">A few basic stats on single commits.</p>
381
+ </div>
382
+
383
+ <% shortest_commit = @commits.min_by { |c| c.subject.length } %>
384
+ <% longest_commit = @commits.max_by { |c| c.subject.length } %>
385
+
386
+ <h2>Shortest commit message</h2>
387
+ <blockquote>
388
+ <p><%=shortest_commit.subject %></p>
389
+ <footer><%=shortest_commit.author.name %> <cite><%=shortest_commit.date.strftime('%a %-d, %B %Y') %></cite></footer>
390
+ </blockquote>
391
+
392
+ <hr/>
393
+
394
+ <h2>Longest commit message</h2>
395
+ <blockquote>
396
+ <p><%=longest_commit.subject %></p>
397
+ <footer><%=longest_commit.author.name %> <cite><%=longest_commit.date.strftime('%a %-d, %B %Y') %></cite></footer>
398
+ </blockquote>
399
+
209
400
  </div>
401
+ <!-- End Commits -->
402
+ <!-- Start Dates -->
403
+ <div class="tab-pane" id="dates">
404
+ <div class="page-header">
405
+ <h1>Dates</h1>
406
+ <p class="lead">When are those commits happening?</p>
407
+ </div>
408
+
409
+ <% first_commit = @commits.min_by(&:date) %>
410
+ <% last_commit = @commits.max_by(&:date) %>
411
+
412
+ <h2>First Commit Ever</h2>
413
+ <blockquote>
414
+ <p><%=first_commit.subject %></p>
415
+ <footer><%=first_commit.author.name %> <cite><%=first_commit.date.strftime('%a %-d, %B %Y') %></cite></footer>
416
+ </blockquote>
417
+
418
+ <hr/>
419
+
420
+ <h2>Last Commit</h2>
421
+ <blockquote>
422
+ <p><%=last_commit.subject %></p>
423
+ <footer><%=last_commit.author.name %> <cite><%=last_commit.date.strftime('%a %-d, %B %Y') %></cite></footer>
424
+ </blockquote>
425
+
426
+ <hr/>
427
+
428
+ <h2>Most Busy Days</h2>
429
+ <canvas id="daysBarChart" width="1100" height="400"></canvas>
430
+
431
+ </div>
432
+ <!-- End Dates -->
210
433
  </div>
211
434
  </div>
212
435
  </div>
@@ -218,63 +441,79 @@
218
441
  </div>
219
442
 
220
443
 
221
- <!-- Bootstrap core JavaScript
444
+ <!-- External JavaScript Files
222
445
  ================================================== -->
223
- <!-- Placed at the end of the document so the pages load faster -->
224
- <script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
225
- <script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
226
- <script src="http://cdnjs.cloudflare.com/ajax/libs/Chart.js/0.2.0/Chart.min.js"></script>
446
+ <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
447
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
448
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.min.js"></script>
227
449
  <script>
228
450
  $('.user-avatar').popover({ trigger: 'hover', html: true }).click(function(e){
229
451
  e.preventDefault();
230
452
  });
231
453
 
232
454
  // Data for Users Pie Chart
233
- var data = [<%= @users.map{|d| "{ value: #{d.commits.count}, color: '#{d.rgba}' }" }.join(',') %>];
234
- var ctx = document.getElementById("usersPieChart").getContext("2d");
235
- var usersPieChart = new Chart(ctx).Pie(data);
236
-
237
- // Data for Insertions vs. Deletions Bar Chart
238
455
  var data = {
239
- labels: [<%= @users.map{|d| "'#{d.escaped_name}'" }.join(',') %>],
240
- datasets: [
241
- {
242
- data: [<%= @users.map{|d| "#{d.total_insertions}" }.join(',') %>],
243
- fillColor: 'rgba(196, 234, 44, 0.5)',
244
- strokeColor: 'rgba(196, 234, 44, 1)'
245
- },
246
- {
247
- data: [<%= @users.map{|d| "#{d.total_deletions}" }.join(',') %>],
248
- fillColor: 'rgba(234, 80, 44, 0.5)',
249
- strokeColor: 'rgba(234, 80, 44, 1)'
250
- }
251
- ]
456
+ datasets: [{
457
+ data: [<%= @users.map{|u| "#{u.commits.count}" }.join(', ') %>],
458
+ backgroundColor: [<%= @users.map{|u| "'#{u.rgba}'" }.join(', ') %>]
459
+ }],
460
+ labels: [<%= @users.map{|u| "'#{u.name}'" }.join(', ') %>
461
+ ]
252
462
  };
253
- var ctx = document.getElementById("usersBarChart").getContext("2d");
254
- var usersBarChart = new Chart(ctx).Bar(data);
463
+ var ctx = document.getElementById("usersPieChart").getContext("2d");
464
+ var usersPieChart = new Chart(ctx,{
465
+ type: 'pie',
466
+ data: data,
467
+ options: {
468
+ legend: {
469
+ display: false
470
+ }
471
+ }
472
+ });
473
+
474
+ // // Data for Insertions vs. Deletions Bar Chart
475
+ // var data = {
476
+ // labels: [<%= @users.map{|d| "'#{d.name}'" }.join(',') %>],
477
+ // datasets: [
478
+ // {
479
+ // data: [<%= @users.map{|d| "#{d.total_insertions}" }.join(',') %>],
480
+ // fillColor: 'rgba(196, 234, 44, 0.5)',
481
+ // strokeColor: 'rgba(196, 234, 44, 1)'
482
+ // },
483
+ // {
484
+ // data: [<%= @users.map{|d| "#{d.total_deletions}" }.join(',') %>],
485
+ // fillColor: 'rgba(234, 80, 44, 0.5)',
486
+ // strokeColor: 'rgba(234, 80, 44, 1)'
487
+ // }
488
+ // ]
489
+ // };
490
+ // var ctx = document.getElementById("usersBarChart").getContext("2d");
491
+ // var usersBarChart = new Chart(ctx).Bar(data);
255
492
 
256
493
  // Data for Weekday commits per user
257
494
  var data = {
258
495
  labels: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
259
496
  datasets: [
260
- <% @users.each_with_index do |user, i| %>
261
497
  {
262
- fillColor : "<%= user.rgba('0.5') %>",
263
- strokeColor : "<%= user.rgba %>",
264
- pointColor : "<%= user.rgba %>",
265
- pointStrokeColor : "#fff",
266
- data : [<%= user.weekday_commits.join(', ') %>]
267
- }<%= ',' unless i + 1 == @users.count %>
268
- <% end %>
498
+ data : [<%= @weekday_commits.join(', ') %>]
499
+ }
269
500
  ]
270
501
  };
271
- var ctx = document.getElementById("usersRadarChart").getContext("2d");
272
- var usersRadarChart = new Chart(ctx).Radar(data);
502
+ var ctx = document.getElementById("daysBarChart").getContext("2d");
503
+ var myBarChart = new Chart(ctx, {
504
+ type: 'bar',
505
+ data: data,
506
+ options: {
507
+ legend: {
508
+ display: false
509
+ }
510
+ }
511
+ });
273
512
 
274
513
  // Makes dashboard items link to tabs
275
514
  $('a[data-open-tab]').click(function(e) {
276
515
  e.preventDefault();
277
- $('a[href="#' + $(this).attr('data-open-tab') + '"]').click();
516
+ $('.navbar-nav').find('a[href="#' + $(this).attr('data-open-tab') + '"]').click();
278
517
  });
279
518
  </script>
280
519
  </body>
data/bin/gitalytics CHANGED
@@ -8,7 +8,9 @@ require 'optparse'
8
8
  require 'gitalytics'
9
9
 
10
10
  options = {
11
- format: 'cli'
11
+ browser: false,
12
+ format: 'cli',
13
+ group_by: 'name'
12
14
  }
13
15
 
14
16
  OptionParser.new do |opts|
@@ -28,7 +30,11 @@ OptionParser.new do |opts|
28
30
  options[:format] = 'html'
29
31
  options[:browser] = false
30
32
  end
33
+
34
+ opts.on("-e", "--group-email", "Group commits by author's email address") do |v|
35
+ options[:group_by] = 'email'
36
+ end
31
37
  end.parse!
32
38
 
33
39
  Dir.chdir($1 || '.')
34
- Gitalytics.new.analyze(options)
40
+ Gitalytics.analyze(options)
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gitalytics/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gitalytics"
8
+ spec.version = Gitalytics::VERSION
9
+
10
+ spec.authors = ["Gonzalo Robaina"]
11
+ spec.email = "gonzalor@gmail.com"
12
+
13
+ spec.summary = "Git Analytics"
14
+ spec.description = "Get usefull analytics from your git log"
15
+ spec.homepage = "http://gonza.uy/gitalytics"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.required_ruby_version = '>= 1.9.3'
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_dependency "color-generator"
28
+ end
data/lib/gitalytics.rb CHANGED
@@ -1,16 +1,18 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  require 'erb'
4
- require 'gitlog'
5
- require 'user'
6
- require 'commit'
4
+ require 'matrix'
5
+ require 'gitalytics/commit'
6
+ require 'gitalytics/gitlog'
7
+ require 'gitalytics/user'
8
+ require 'gitalytics/version'
7
9
 
8
- class Gitalytics
10
+ module Gitalytics
9
11
 
10
- VERSION = '1.2.2'
12
+ module_function
11
13
 
12
14
  def analyze(options)
13
- data = GitLog.parse_git_log
15
+ data = GitLog.parse_git_log(options[:group_by])
14
16
 
15
17
  case options[:format]
16
18
  when 'html'
@@ -22,6 +24,8 @@ class Gitalytics
22
24
 
23
25
  private
24
26
 
27
+ module_function
28
+
25
29
  def output_cli_report(users)
26
30
  users.each do |user|
27
31
  puts user.summary
@@ -37,6 +41,9 @@ class Gitalytics
37
41
  y.commits.length <=> x.commits.length
38
42
  end
39
43
  @commits = data[:commits]
44
+ weekday_commits = @users.map(&:weekday_commits)
45
+ @weekday_commits = weekday_commits.map { |a| Vector[*a] }.inject(:+).to_a
46
+
40
47
  file.write(erb.result(binding))
41
48
  end
42
49
  open_report_in_browser(output_file) if open_in_browser
@@ -52,5 +59,4 @@ class Gitalytics
52
59
  system "xdg-open #{filename}"
53
60
  end
54
61
  end
55
-
56
62
  end
File without changes
@@ -1,22 +1,22 @@
1
- require 'commit'
2
- require 'user'
1
+ require 'gitalytics/commit'
2
+ require 'gitalytics/user'
3
3
  require 'date'
4
4
 
5
5
  module GitLog
6
6
 
7
7
  module_function
8
8
 
9
- def parse_git_log
9
+ def parse_git_log(group_by)
10
10
  users, commits = [], []
11
11
 
12
12
  log = get_log
13
13
  blocks = get_blocks(log)
14
14
 
15
15
  blocks.each do |(hash, block_string)|
16
- commits << parse_block(hash, block_string, users)
16
+ commits << parse_block(hash, block_string, users, group_by)
17
17
  end
18
18
 
19
- { users: users, commits: commits }
19
+ {users: users, commits: commits}
20
20
  end
21
21
 
22
22
  def get_log
@@ -31,7 +31,7 @@ module GitLog
31
31
  commits.zip(blocks)
32
32
  end
33
33
 
34
- def parse_block(hash, block_string, users)
34
+ def parse_block(hash, block_string, users, group_by)
35
35
  commit = Commit.new(hash)
36
36
 
37
37
  block_string.encode!('UTF-8', 'UTF8-MAC') if defined?(Encoding::UTF8_MAC)
@@ -41,7 +41,7 @@ module GitLog
41
41
  commit.subject = data[:subject]
42
42
  commit.date = Date.parse(data[:date])
43
43
 
44
- get_commit_author(data, commit, users)
44
+ get_commit_author(data, commit, users, group_by)
45
45
  get_commit_summary(block_string, commit)
46
46
 
47
47
  commit
@@ -57,16 +57,22 @@ module GitLog
57
57
  end
58
58
  end
59
59
 
60
- def get_commit_author(data, commit, users)
61
- user = get_user(data[:name], data[:email], users)
60
+ def get_commit_author(data, commit, users, group_by)
61
+ user = get_user(data[:name], data[:email], users, group_by)
62
62
 
63
63
  commit.author = user
64
64
  user.commits << commit
65
65
  end
66
66
 
67
- def get_user(name, email, users)
68
- users.each do |user|
69
- return user if user.email == email
67
+ def get_user(name, email, users, group_by)
68
+ if group_by == 'name'
69
+ users.each do |user|
70
+ return user if user.name == name
71
+ end
72
+ elsif group_by == 'email'
73
+ users.each do |user|
74
+ return user if user.email == email
75
+ end
70
76
  end
71
77
  users << new_user = User.new(name, email)
72
78
  new_user
@@ -1,19 +1,15 @@
1
+ require 'color-generator'
1
2
  require 'digest/md5'
2
- require 'CGI'
3
3
 
4
4
  class User
5
5
 
6
6
  attr_accessor :name, :email, :commits, :colors
7
7
 
8
8
  def initialize(name, email)
9
- self.name = name
10
- self.email = email
9
+ self.name = name
10
+ self.email = email
11
11
  self.commits = []
12
- self.colors = [rand(255), rand(255), rand(255)].join(', ')
13
- end
14
-
15
- def escaped_name
16
- CGI.escapeHTML(name)
12
+ self.colors = ColorGenerator.new(saturation: 0.3, lightness: 0.75).create_rgb.join(', ')
17
13
  end
18
14
 
19
15
  def gravatar
@@ -44,8 +40,12 @@ class User
44
40
  commits.map(&:deletions).inject(0) { |total, current| total + current }
45
41
  end
46
42
 
43
+ def total_changes
44
+ total_insertions + total_deletions
45
+ end
46
+
47
47
  def summary
48
- "#{name} has made #{commits.count} commits between #{commits_period} days. He/she did something useful on #{working_days} of those days."
48
+ "#{name} has made #{commits.count} commits on #{working_days} separate days during a span of #{commits_period} days."
49
49
  end
50
50
 
51
51
  def rgba(opacity = 1)
@@ -54,8 +54,9 @@ class User
54
54
 
55
55
  def weekday_commits
56
56
  days = Array.new(7) {0}
57
- commits.each { |c| days[c.date.wday] += 1 }
57
+ commits.each do |c|
58
+ days[c.date.wday] += 1
59
+ end
58
60
  days
59
61
  end
60
-
61
62
  end
@@ -0,0 +1,3 @@
1
+ module Gitalytics
2
+ VERSION = "1.3.0"
3
+ end
@@ -0,0 +1,42 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper.rb')
2
+ require 'minitest/autorun'
3
+ require 'gitalytics/commit'
4
+
5
+ class TestCommit < MiniTest::Unit::TestCase
6
+
7
+ def setup
8
+ @commit = Commit.new('abcdef0123456789')
9
+ end
10
+
11
+ def build_commit(commit)
12
+ commit.summary << {
13
+ insertions: 3,
14
+ deletions: 2,
15
+ filename: 'a.rb'
16
+ }
17
+ commit.summary << {
18
+ insertions: 4,
19
+ deletions: 3,
20
+ filename: 'b.rb'
21
+ }
22
+ end
23
+
24
+ def test_that_count_insertions
25
+ build_commit(@commit)
26
+
27
+ assert_equal(7, @commit.insertions)
28
+ end
29
+
30
+ def test_that_count_deletions
31
+ build_commit(@commit)
32
+
33
+ assert_equal(5, @commit.deletions)
34
+ end
35
+
36
+ def test_that_count_files_committed
37
+ build_commit(@commit)
38
+
39
+ assert_equal(['a.rb', 'b.rb'], @commit.files_committed)
40
+ end
41
+
42
+ end
@@ -0,0 +1,105 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper.rb')
2
+ require 'minitest/autorun'
3
+ require 'gitalytics/gitlog'
4
+
5
+ class TestCommit < MiniTest::Unit::TestCase
6
+
7
+ def git_log
8
+
9
+ %q(bea63e76c7c6d5afd42ac5b24a911b36e5c261f9 2013-02-13 23:11:16 -0200 Gonzalo Robaina <gonzalor@gmail.com> Print basic commit report on the console
10
+
11
+ 6 1 bin/gitalytics
12
+ 33 6 lib/gitalytics.rb
13
+ d392710a9e657c72e73ca87df3ed2c8c802441e4 2012-12-19 06:04:59 -0800 Gonzalo Robaina <gonzalor@gmail.com> Initial commit
14
+
15
+ 16 0 .gitignore
16
+ 4 0 README.md
17
+ 9a99ae5dbc1eaadf268ca925cce9b68d10216151 2012-12-06 10:29:49 -0200 Gonzalo Robaina <gonzalor@gmail.com> initial commit
18
+
19
+ 1 0 README.md
20
+ 3 0 bin/gitalytics
21
+ 13 0 gitalytics.gemspec
22
+ 49 0 lib/gist.rb
23
+ 11 0 lib/gitalytics.rb
24
+ )
25
+ end
26
+
27
+ def test_get_blocks
28
+ expected_blocks = [
29
+ %q(2013-02-13 23:11:16 -0200 Gonzalo Robaina <gonzalor@gmail.com> Print basic commit report on the console
30
+
31
+ 6 1 bin/gitalytics
32
+ 33 6 lib/gitalytics.rb),
33
+
34
+ %q(2012-12-19 06:04:59 -0800 Gonzalo Robaina <gonzalor@gmail.com> Initial commit
35
+
36
+ 16 0 .gitignore
37
+ 4 0 README.md),
38
+
39
+ %q(2012-12-06 10:29:49 -0200 Gonzalo Robaina <gonzalor@gmail.com> initial commit
40
+
41
+ 1 0 README.md
42
+ 3 0 bin/gitalytics
43
+ 13 0 gitalytics.gemspec
44
+ 49 0 lib/gist.rb
45
+ 11 0 lib/gitalytics.rb)
46
+ ]
47
+
48
+ expected_commits = [
49
+ 'bea63e76c7c6d5afd42ac5b24a911b36e5c261f9',
50
+ 'd392710a9e657c72e73ca87df3ed2c8c802441e4',
51
+ '9a99ae5dbc1eaadf268ca925cce9b68d10216151',
52
+ ]
53
+ blocks = GitLog.get_blocks(git_log)
54
+
55
+ assert_equal(expected_commits, blocks.map(&:first))
56
+ assert_equal(expected_blocks, blocks.map(&:last))
57
+ end
58
+
59
+ def test_parse_block
60
+ blocks = GitLog.get_blocks(git_log)
61
+ hash, block = blocks.last
62
+
63
+ users = []
64
+ commit = GitLog.parse_block(hash, block, users, 'email')
65
+
66
+ assert_equal(
67
+ ['README.md', 'bin/gitalytics', 'gitalytics.gemspec', 'lib/gist.rb', 'lib/gitalytics.rb'],
68
+ commit.files_committed
69
+ )
70
+ assert_equal(77, commit.insertions)
71
+ assert_equal(0, commit.deletions)
72
+ assert_equal('Gonzalo Robaina', commit.author.name)
73
+ assert_equal('gonzalor@gmail.com', commit.author.email)
74
+ assert_equal('initial commit', commit.subject)
75
+ end
76
+
77
+ def test_get_user_creates_the_user
78
+ users = []
79
+ user = GitLog.get_user('name', 'email@example.com', users, 'name')
80
+
81
+ assert_equal(1, users.count)
82
+ assert_equal(user, users.last)
83
+ assert_equal('name', user.name)
84
+ assert_equal('email@example.com', user.email)
85
+ end
86
+
87
+ def test_get_user_returns_the_user
88
+ users = [User.new('name', 'email@example.com')]
89
+
90
+ user = GitLog.get_user('name', 'email@example.com', users, 'email')
91
+ assert_equal(user, users.last)
92
+ assert_equal('name', user.name)
93
+ assert_equal('email@example.com', user.email)
94
+ end
95
+
96
+ def test_get_commit_author_links_user_and_commit
97
+ data = { name: 'test', email: 'test@email.com' }
98
+ commit = Commit.new(hash)
99
+ users = []
100
+
101
+ GitLog.get_commit_author(data, commit, users, 'name')
102
+ assert_equal(users.last, commit.author)
103
+ assert_equal(users.last.commits.last, commit)
104
+ end
105
+ end
@@ -0,0 +1,86 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper.rb')
2
+ require 'minitest/autorun'
3
+ require 'gitalytics/user'
4
+ require 'gitalytics/commit'
5
+ require 'date'
6
+
7
+ class TestUser < MiniTest::Unit::TestCase
8
+
9
+ def setup
10
+ @user = User.new('John Doe', 'john@doe.com')
11
+ end
12
+
13
+ def create_commit(hash, date, summary_data)
14
+ Commit.new(hash).tap do |commit|
15
+ commit.date = Date.parse(date)
16
+ summary_data.each { |summary| commit.summary << summary }
17
+ end
18
+ end
19
+
20
+ def create_commits(user)
21
+ user.commits << create_commit('abcdef', '2000-01-10', [{ # weekday: 1
22
+ filename: 'a.rb',
23
+ insertions: 1,
24
+ deletions: 0,
25
+ }])
26
+ user.commits << create_commit('abcdef', '2000-01-01', [{ # weekday: 6
27
+ filename: 'c.rb',
28
+ insertions: 0,
29
+ deletions: 1,
30
+ }])
31
+ user.commits << create_commit('abcdef', '2000-01-01', [{ # weekday: 6
32
+ filename: 'd.rb',
33
+ insertions: 4,
34
+ deletions: 5
35
+ }])
36
+ user.commits << create_commit('abcdef', '2000-01-08', [{ # weekday: 6
37
+ filename: 'e.rb',
38
+ insertions: 1,
39
+ deletions: 1
40
+ }])
41
+ end
42
+
43
+ def test_initial_color
44
+ assert_match(/\d{1,3}, \d{1,3}, \d{1,3}/, @user.colors)
45
+ end
46
+
47
+ def test_gravatar_is_md5
48
+ assert_match(/[a-f0-9]{32}/, @user.gravatar)
49
+ end
50
+
51
+ def test_first_commit
52
+ create_commits(@user)
53
+
54
+ assert_equal(Date.parse('2000-1-1'), @user.first_commit.date)
55
+ end
56
+
57
+ def test_last_commit
58
+ create_commits(@user)
59
+
60
+ assert_equal(Date.parse('2000-1-10'), @user.last_commit.date)
61
+ end
62
+
63
+ def test_working_days
64
+ create_commits(@user)
65
+
66
+ assert_equal(3, @user.working_days)
67
+ end
68
+
69
+ def test_weekday_commits
70
+ create_commits(@user)
71
+
72
+ assert_equal([0, 1, 0, 0, 0, 0, 3], @user.weekday_commits)
73
+ end
74
+
75
+ def test_total_insertions
76
+ create_commits(@user)
77
+
78
+ assert_equal(6, @user.total_insertions)
79
+ end
80
+
81
+ def test_total_deletions
82
+ create_commits(@user)
83
+
84
+ assert_equal(7, @user.total_deletions)
85
+ end
86
+ end
@@ -0,0 +1,2 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitalytics
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.3.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: 2015-10-19 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2016-07-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: color-generator
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
13
55
  description: Get usefull analytics from your git log
14
56
  email: gonzalor@gmail.com
15
57
  executables:
@@ -17,13 +59,27 @@ executables:
17
59
  extensions: []
18
60
  extra_rdoc_files: []
19
61
  files:
62
+ - ".coveralls.yml"
63
+ - ".gitignore"
64
+ - ".travis.yml"
65
+ - CHANGELOG.md
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - README.md
69
+ - Rakefile
20
70
  - assets/gitalytics.html.erb
21
71
  - bin/gitalytics
22
- - lib/commit.rb
72
+ - gitalytics.gemspec
23
73
  - lib/gitalytics.rb
24
- - lib/gitlog.rb
25
- - lib/user.rb
26
- homepage: http://rubygems.org/gems/gitalytics
74
+ - lib/gitalytics/commit.rb
75
+ - lib/gitalytics/gitlog.rb
76
+ - lib/gitalytics/user.rb
77
+ - lib/gitalytics/version.rb
78
+ - test/lib/test_commit.rb
79
+ - test/lib/test_gitlog.rb
80
+ - test/lib/test_user.rb
81
+ - test/test_helper.rb
82
+ homepage: http://gonza.uy/gitalytics
27
83
  licenses:
28
84
  - MIT
29
85
  metadata: {}
@@ -33,18 +89,22 @@ require_paths:
33
89
  - lib
34
90
  required_ruby_version: !ruby/object:Gem::Requirement
35
91
  requirements:
36
- - - '>='
92
+ - - ">="
37
93
  - !ruby/object:Gem::Version
38
- version: '0'
94
+ version: 1.9.3
39
95
  required_rubygems_version: !ruby/object:Gem::Requirement
40
96
  requirements:
41
- - - '>='
97
+ - - ">="
42
98
  - !ruby/object:Gem::Version
43
99
  version: '0'
44
100
  requirements: []
45
101
  rubyforge_project:
46
- rubygems_version: 2.4.6
102
+ rubygems_version: 2.4.3
47
103
  signing_key:
48
104
  specification_version: 4
49
105
  summary: Git Analytics
50
- test_files: []
106
+ test_files:
107
+ - test/lib/test_commit.rb
108
+ - test/lib/test_gitlog.rb
109
+ - test/lib/test_user.rb
110
+ - test/test_helper.rb