gitalytics 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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