gitalytics 0.0.2 → 1.0.1
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 +7 -0
- data/assets/gitalytics.html.erb +161 -0
- data/bin/gitalytics +18 -3
- data/lib/gitalytics.rb +52 -23
- metadata +23 -42
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cce45293feabfcfb6ee420eab9cfac9d300e6c60
|
4
|
+
data.tar.gz: 8186c38edf85d533bc7e93be902723556d9f2785
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db1c0258b22930f52b8add537e0d140c84b371e408d8bed6b47dc88edcfaff01b43cbc0f46da024bb0e35514e73252151eb5486e55c223ee5224aa4966369ebd
|
7
|
+
data.tar.gz: b23d34ab57f25adffdf315edc2d81056287ed9fc79b043c74ed6011f54ae3740da5d37c8655df69bf02e564b952dccea3eaa9a25cc16714ab0361494cf678c98
|
@@ -0,0 +1,161 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
+
<meta name="description" content="">
|
8
|
+
<meta name="author" content="">
|
9
|
+
|
10
|
+
<title>Gitalytics report</title>
|
11
|
+
|
12
|
+
<!-- Bootstrap core CSS -->
|
13
|
+
<link href="http://netdna.bootstrapcdn.com/bootswatch/3.0.3/cerulean/bootstrap.min.css" rel="stylesheet">
|
14
|
+
|
15
|
+
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
16
|
+
<!--[if lt IE 9]>
|
17
|
+
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
|
18
|
+
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
|
19
|
+
<![endif]-->
|
20
|
+
|
21
|
+
<style>
|
22
|
+
/* Sticky footer styles
|
23
|
+
-------------------------------------------------- */
|
24
|
+
|
25
|
+
html,
|
26
|
+
body {
|
27
|
+
height: 100%;
|
28
|
+
/* The html and body elements cannot have any padding or margin. */
|
29
|
+
}
|
30
|
+
|
31
|
+
/* Wrapper for page content to push down footer */
|
32
|
+
#wrap {
|
33
|
+
min-height: 100%;
|
34
|
+
height: auto;
|
35
|
+
/* Negative indent footer by its height */
|
36
|
+
margin: 0 auto -60px;
|
37
|
+
/* Pad bottom by footer height */
|
38
|
+
padding: 0 0 60px;
|
39
|
+
}
|
40
|
+
|
41
|
+
/* Set the fixed height of the footer here */
|
42
|
+
#footer {
|
43
|
+
height: 60px;
|
44
|
+
background-color: #f5f5f5;
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
/* Custom page CSS
|
49
|
+
-------------------------------------------------- */
|
50
|
+
/* Not required for template or sticky footer method. */
|
51
|
+
|
52
|
+
#wrap > .container {
|
53
|
+
padding: 60px 15px 0;
|
54
|
+
}
|
55
|
+
.container .text-muted {
|
56
|
+
margin: 20px 0;
|
57
|
+
}
|
58
|
+
|
59
|
+
#footer > .container {
|
60
|
+
padding-left: 15px;
|
61
|
+
padding-right: 15px;
|
62
|
+
}
|
63
|
+
|
64
|
+
.media {
|
65
|
+
margin-bottom: 20px;
|
66
|
+
}
|
67
|
+
|
68
|
+
.dp {
|
69
|
+
box-sizing: border-box;
|
70
|
+
border:10px solid #eee;
|
71
|
+
transition: all 0.2s ease-in-out;
|
72
|
+
}
|
73
|
+
|
74
|
+
.dp:hover {
|
75
|
+
border:2px solid #eee;
|
76
|
+
transform:rotate(360deg);
|
77
|
+
-ms-transform:rotate(360deg);
|
78
|
+
-webkit-transform:rotate(360deg);
|
79
|
+
/*-webkit-font-smoothing:antialiased;*/
|
80
|
+
}
|
81
|
+
|
82
|
+
</style>
|
83
|
+
</head>
|
84
|
+
|
85
|
+
<body>
|
86
|
+
|
87
|
+
<!-- Wrap all page content here -->
|
88
|
+
<div id="wrap">
|
89
|
+
|
90
|
+
<!-- Fixed navbar -->
|
91
|
+
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
|
92
|
+
<div class="container">
|
93
|
+
<div class="navbar-header">
|
94
|
+
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
95
|
+
<span class="sr-only">Toggle navigation</span>
|
96
|
+
<span class="icon-bar"></span>
|
97
|
+
<span class="icon-bar"></span>
|
98
|
+
<span class="icon-bar"></span>
|
99
|
+
</button>
|
100
|
+
<a class="navbar-brand" href="#">Gitalytics</a>
|
101
|
+
</div>
|
102
|
+
<div class="collapse navbar-collapse">
|
103
|
+
<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> -->
|
107
|
+
</ul>
|
108
|
+
</div><!--/.nav-collapse -->
|
109
|
+
</div>
|
110
|
+
</div>
|
111
|
+
|
112
|
+
<!-- Begin page content -->
|
113
|
+
<div class="container">
|
114
|
+
<div class="page-header">
|
115
|
+
<h1>User stats</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
|
+
</div>
|
118
|
+
|
119
|
+
<div class="col-md-5">
|
120
|
+
<canvas id="myChart" width="400" height="400"></canvas>
|
121
|
+
</div>
|
122
|
+
<div class="col-md-7">
|
123
|
+
<% @data[:users].each_with_index do |user, i| %>
|
124
|
+
<div class="col-md-6">
|
125
|
+
<div class="media">
|
126
|
+
<a class="pull-left" href="#">
|
127
|
+
<img class="media-object dp img-circle" src="<%= "http://www.gravatar.com/avatar/#{user[:gravatar]}" %>" style="width: 100px; height:100px; border-color:<%= "##{user[:color]}" %>">
|
128
|
+
</a>
|
129
|
+
<div class="media-body">
|
130
|
+
<h4 class="media-heading"><%= user[:name] %></h4>
|
131
|
+
<hr style="margin:8px auto">
|
132
|
+
<%= "#{user[:commits].count} commits between #{(user[:last_date] - user[:first_date]).to_i + 1} days." %>
|
133
|
+
<%= "He did something useful on #{user[:working_days]} of those days." %>
|
134
|
+
</div>
|
135
|
+
</div>
|
136
|
+
</div>
|
137
|
+
<% end %>
|
138
|
+
</div>
|
139
|
+
</div>
|
140
|
+
</div>
|
141
|
+
|
142
|
+
<div id="footer">
|
143
|
+
<div class="container">
|
144
|
+
<p class="text-muted">Gitalytics by <a href="http://gonzalo.robaina.me">Gonzalo Robaina</a></p>
|
145
|
+
</div>
|
146
|
+
</div>
|
147
|
+
|
148
|
+
|
149
|
+
<!-- Bootstrap core JavaScript
|
150
|
+
================================================== -->
|
151
|
+
<!-- Placed at the end of the document so the pages load faster -->
|
152
|
+
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
|
153
|
+
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
|
154
|
+
<script src="http://cdnjs.cloudflare.com/ajax/libs/Chart.js/0.2.0/Chart.min.js"></script>
|
155
|
+
<script>
|
156
|
+
var data = [<%= @data[:users].map{|d| "{ value: #{d[:commits].count}, color: '##{d[:color]}' }" }.join(',') %>];
|
157
|
+
var ctx = document.getElementById("myChart").getContext("2d");
|
158
|
+
var myNewChart = new Chart(ctx).Pie(data);
|
159
|
+
</script>
|
160
|
+
</body>
|
161
|
+
</html>
|
data/bin/gitalytics
CHANGED
@@ -1,7 +1,22 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
2
3
|
require File.expand_path("../../lib/gitalytics.rb", __FILE__)
|
3
4
|
|
5
|
+
options = {
|
6
|
+
format: 'cli'
|
7
|
+
}
|
8
|
+
OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: gitalytics [options]"
|
10
|
+
|
11
|
+
opts.on("-v", "--version", "Display gem version") do |v|
|
12
|
+
p "Gitalytics #{Gitalytics::VERSION}"
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
|
16
|
+
opts.on("-h", "--html", "Outputs html report") do |v|
|
17
|
+
options[:format] = 'html'
|
18
|
+
end
|
19
|
+
end.parse!
|
20
|
+
|
4
21
|
Dir.chdir($1 || '.')
|
5
|
-
|
6
|
-
res = g.log_to_hash
|
7
|
-
g.output_summary_report(res)
|
22
|
+
Gitalytics.new.analyze(options)
|
data/lib/gitalytics.rb
CHANGED
@@ -1,40 +1,69 @@
|
|
1
1
|
require 'date'
|
2
|
+
require 'erb'
|
3
|
+
require 'digest/md5'
|
2
4
|
|
3
5
|
class Gitalytics
|
6
|
+
|
7
|
+
VERSION = '1.0.1'
|
8
|
+
|
9
|
+
attr_accessor :data
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@data = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def analyze(options)
|
16
|
+
log_to_hash
|
17
|
+
case options[:format]
|
18
|
+
when 'html'
|
19
|
+
output_html_report
|
20
|
+
else
|
21
|
+
output_cli_report
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
4
26
|
def log_to_hash
|
5
27
|
lines = []
|
6
|
-
command = "git log --pretty='%cn|%cd|%s'"
|
28
|
+
command = "git log --pretty='%cn|%ce|%cd|%s'"
|
7
29
|
result = `#{command}`
|
8
30
|
|
9
31
|
result.each_line do |line|
|
10
32
|
parts = line.split('|')
|
11
|
-
lines << {:
|
33
|
+
lines << { name: parts[0], email: parts[1], date: Date.parse(parts[2]), subject: parts[3]}
|
12
34
|
end
|
13
35
|
|
14
|
-
|
15
|
-
:users => lines.map{|r| r[:username]}.uniq,
|
16
|
-
:dates => lines.map{|r| r[:date]}.uniq,
|
17
|
-
:records => lines
|
18
|
-
}
|
36
|
+
@data[:users] = get_user_data(lines)
|
19
37
|
end
|
20
38
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
dates_for_user = res[:records].select{|r| r[:username] == user}.map{|r| r[:date]}
|
25
|
-
commit_count = dates_for_user.count
|
26
|
-
date_count = dates_for_user.uniq.count
|
27
|
-
first_date = dates_for_user.min
|
28
|
-
last_date = dates_for_user.max
|
29
|
-
puts "#{user} has made #{commit_count} commits over the last #{last_date-first_date} days. He did something useful on #{date_count} of those days."
|
39
|
+
def output_cli_report
|
40
|
+
@data[:users].each do |user|
|
41
|
+
puts "#{user[:name]} has made #{user[:commits].count} commits between #{(user[:last_date] - user[:first_date]).to_i + 1} days. He did something useful on #{user[:working_days]} of those days."
|
30
42
|
end
|
31
43
|
end
|
32
|
-
end
|
33
44
|
|
34
|
-
|
35
|
-
|
36
|
-
|
45
|
+
def output_html_report
|
46
|
+
template_file = File.read(File.join(File.dirname(__FILE__), "..", "assets", "gitalytics.html.erb"))
|
47
|
+
erb = ERB.new(template_file)
|
48
|
+
File.open("gitalytics_result.html", 'w+') { |file| file.write(erb.result(binding)) }
|
49
|
+
end
|
37
50
|
|
38
|
-
|
39
|
-
|
40
|
-
|
51
|
+
def get_user_data(lines)
|
52
|
+
users = lines.map{ |r| [r[:name], r[:email]] }.uniq
|
53
|
+
users.map{ |u|
|
54
|
+
commits = lines.select{ |r| r[:name] == u[0] }
|
55
|
+
dates = commits.map{ |c| c[:date] }
|
56
|
+
{
|
57
|
+
name: u[0],
|
58
|
+
email: u[1],
|
59
|
+
gravatar: Digest::MD5.hexdigest(u[1]),
|
60
|
+
color: "%06x" % (rand * 0xffffff),
|
61
|
+
commits: commits,
|
62
|
+
first_date: dates.min,
|
63
|
+
last_date: dates.max,
|
64
|
+
working_days: dates.uniq.count
|
65
|
+
}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
metadata
CHANGED
@@ -1,66 +1,47 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitalytics
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 2
|
10
|
-
version: 0.0.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Gonzalo Robaina
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2013-12-27 00:00:00 Z
|
11
|
+
date: 2013-12-27 00:00:00.000000000 Z
|
19
12
|
dependencies: []
|
20
|
-
|
21
13
|
description: Get usefull analytics from your git log
|
22
14
|
email: gonzalor@gmail.com
|
23
|
-
executables:
|
15
|
+
executables:
|
24
16
|
- gitalytics
|
25
17
|
extensions: []
|
26
|
-
|
27
18
|
extra_rdoc_files: []
|
28
|
-
|
29
|
-
files:
|
19
|
+
files:
|
30
20
|
- lib/gitalytics.rb
|
21
|
+
- assets/gitalytics.html.erb
|
31
22
|
- bin/gitalytics
|
32
23
|
homepage: http://rubygems.org/gems/gitalytics
|
33
|
-
licenses:
|
24
|
+
licenses:
|
34
25
|
- MIT
|
26
|
+
metadata: {}
|
35
27
|
post_install_message:
|
36
28
|
rdoc_options: []
|
37
|
-
|
38
|
-
require_paths:
|
29
|
+
require_paths:
|
39
30
|
- lib
|
40
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
none: false
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
hash: 3
|
55
|
-
segments:
|
56
|
-
- 0
|
57
|
-
version: "0"
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - '>='
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
58
41
|
requirements: []
|
59
|
-
|
60
42
|
rubyforge_project:
|
61
|
-
rubygems_version: 1.
|
43
|
+
rubygems_version: 2.1.10
|
62
44
|
signing_key:
|
63
|
-
specification_version:
|
45
|
+
specification_version: 4
|
64
46
|
summary: Git Analytics
|
65
47
|
test_files: []
|
66
|
-
|