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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 440abf11cbc39d3544a6d226527dd29dfb4d2adf
4
- data.tar.gz: fc5393c1b3ea97dd5819f1d607b879d085a18fa5
3
+ metadata.gz: dfee4701c631f00b32ae513f27dd761e78bc90c9
4
+ data.tar.gz: 18dfec6a19254bd61de7ecb7a65b34b824f610f9
5
5
  SHA512:
6
- metadata.gz: 56972f5d972a6d17f5bb46c234d27ffa6865dc2be24d636a0159147be2753726331113a725cbbee862d3a83e3a11c4bf0b93f0d506fc0eecdb2b2878ee33f6e9
7
- data.tar.gz: 3e825ed9e7103ff3d543ce2c870d1ca153b7ba2389f6ae5047e3d124fc748bef25baca3199e4181acf19104251c04abc4474ff3756cb0be566d94b0fdec24688
6
+ metadata.gz: 22ab60c1cc856e08152c8893170c567b82831ff09910c7d4a7e7107cc06576042651ef0001a71a7533da5557b4d31a0ab3d515fe897fb07531e32b62ce1d45b1
7
+ data.tar.gz: f24764d467fd4e7df3f06a37804f3185d2e07157241e38764ff70c4e013ccef02c81377c1459998d1f75e1daf3f729d4c560d8395b8e26e8eff50f0a1c24b9b0
@@ -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="#">Users</a></li>
105
- <!-- <li><a href="#about">About</a></li>
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="page-header">
115
- <h1>Users</h1>
116
- <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>
117
- <% @data[:users].each do |user| %>
118
- <a href="#" title="<%= user.name %> <small><%= user.email %></small>" class="user-avatar" data-content="<%= user.summary %>">
119
- <img class="dp img-circle" src="<%= "http://www.gravatar.com/avatar/#{user.gravatar}" %>" style="width: 100px; height:100px; border-color:<%= user.rgba %>"></a>
120
- <% end %>
121
- </div>
122
-
123
- <div class="row">
124
- <div class="col-md-6">
125
- <h2>Commits count per user</h2>
126
- <canvas id="usersPieChart" width="550" height="400"></canvas>
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="col-md-6">
129
- <h2>Commits per weekday per user</h2>
130
- <canvas id="usersRadarChart" width="550" height="400"></canvas>
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 = [<%= @data[:users].map{|d| "{ value: #{d.commits.count}, color: '#{d.rgba}' }" }.join(',') %>];
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: [<%= @data[:users].map{|d| "'#{d.name}'" }.join(',') %>],
232
+ labels: [<%= @users.map{|d| "'#{d.name}'" }.join(',') %>],
165
233
  datasets: [
166
234
  {
167
- data: [<%= @data[:users].map{|d| "#{d.total_insertions}" }.join(',') %>],
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: [<%= @data[:users].map{|d| "#{d.total_deletions}" }.join(',') %>],
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
- <% @data[:users].each_with_index do |user, i| %>
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 == @data[:users].count %>
260
+ }<%= ',' unless i + 1 == @users.count %>
193
261
  <% end %>
194
262
  ]
195
263
  };
@@ -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 File.expand_path("../../lib/gitalytics.rb", __FILE__)
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
 
@@ -4,144 +4,54 @@ require 'date'
4
4
  require 'erb'
5
5
  require 'digest/md5'
6
6
 
7
- class Gitalytics
8
-
9
- VERSION = '1.1.1'
7
+ require 'gitlog'
8
+ require 'user'
9
+ require 'commit'
10
10
 
11
- attr_accessor :data
11
+ class Gitalytics
12
12
 
13
- def initialize
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
- if match = line.match(/^commit ([0-9a-z]*)$/)
35
- @data[:commits] << Commit.new(match[1])
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
- File.open("gitalytics_result.html", 'w+') { |file| file.write(erb.result(binding)) }
61
- end
62
-
63
- def get_user(name, email)
64
- @data[:users].each do |u|
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
- @data[:users] << new_user = User.new(name, email)
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 summary
113
- "#{name} has made #{commits.count} commits between #{commits_period} days. He/she did something useful on #{working_days} of those days."
114
- end
115
-
116
- def rgba(opacity = 1)
117
- "rgba(#{colors}, #{opacity})"
118
- end
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.1.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-07 00:00:00.000000000 Z
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