Commit 48235df6339c24e99a70baa8a7e999f659cbb5a5

Use googlechart instead of gruff

Commit diff

app/controllers/projects_controller.rb

 
1
2require 'gruff'
3
41class ProjectsController < ApplicationController
52 before_filter :login_required, :only => [:create, :update, :destroy, :new]
63 before_filter :require_user_has_ssh_keys, :only => [:new, :create]
8282 end
8383 redirect_to projects_path
8484 end
85
86 def commit_graph
87 @project = Project.find_by_slug!(params[:id])
88 sha = params[:sha] ? params[:sha] : "master"
89
90 repo = @project.mainline_repository
91 git_repo = repo.git
92 git = git_repo.git
93
94 width = params[:width] ? params[:width].to_i : 250
95
96 h = Hash.new
97 dategroup = Date.new
98
99 data = git.rev_list({:pretty => "format:%aD", :since => "24 weeks ago"}, sha)
100 data.each_line { |line|
101 if line =~ /\d\d:\d\d:\d\d/ then
102 date = Date.parse(line)
103
104 dategroup = Date.new(date.year, date.month, 1)
105 if h[dategroup]
106 h[dategroup] += 1
107 else
108 h[dategroup] = 1
109 end
110 end
111 }
112
113 g = Gruff::Line.new(width)
114 setup_gruff(g)
115 g.hide_legend = true
116 g.title = "#{@project.title} commits"
117
118 commits = []
119 labels = {}
120 it = 0
121 h.sort.each { |entry|
122 date = entry.first
123 value = entry.last
124
125 labels[it] = date.strftime("%m/%y")
126 commits << value
127 it+=1
128 }
129
130 unless commits.empty?
131 g.data("Commits", commits)
132 g.labels = labels
133# g.labels = { 0 => labels.first, commits.size-1 => labels.last }
134 end
135
136 send_data(g.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.slug}-commits.png")
137 end
138
139 def commit_graph_by_author
140 @project = Project.find_by_slug!(params[:id])
141 sha = params[:sha] ? params[:sha] : "master"
142
143 repo = @project.mainline_repository
144 git_repo = repo.git
145 git = git_repo.git
146
147 width = params[:width] ? params[:width].to_i : 400
148
149 h = Hash.new
150
151 data = git.rev_list({:pretty => "format:name:%cn", :since => "1 years ago" }, sha)
152 data.each_line { |line|
153 if line =~ /^name:(.*)$/ then
154 author = $1
155
156 if h[author]
157 h[author] += 1
158 else
159 h[author] = 1
160 end
161 end
162 }
163
164 g = Gruff::Pie.new(width)
165 setup_gruff(g)
166 g.title = "#{@project.title}"
167
168 sorted = h.sort_by { |author, commits|
169 commits
170 }
171
172 label_it = 0
173 labels = {}
174 max = 5
175 others = []
176 top = sorted
177
178 if sorted.size > max
179 top = sorted[sorted.size-max, sorted.size]
180 others = sorted[0, sorted.size-max]
181 end
182
183 top.each { |entry|
184 v = entry.last
185 g.data(entry.first, [v])
186
187 labels[label_it] = v
188 label_it += 1
189 }
190
191 unless others.empty?
192 others_v = others.inject { |v, acum| [v.last + acum.last] }
193 labels[label_it] = others_v.first
194 g.data("others", others_v )
195 end
196
197 g.labels = labels
198
199 send_data(g.to_blob, :disposition => 'inline', :type => 'image/png', :filename => "#{@project.slug}-commits_author.png")
200 end
201
202 private
203 def setup_gruff(g)
204 g.no_data_message = "No commits"
205
206 colors = [
207 '#a9dada', # blue
208 '#aedaa9', # green
209 '#dadaa9', # yellow
210 '#daaea9', # peach
211 '#a9a9da', # dk purple
212 '#daaeda', # purple
213 '#dadada' # grey
214 ]
215
216 g.theme = {
217 :colors => colors,
218 :marker_color => '#aea9a9', # Grey
219 :font_color => 'black',
220 :background_colors => '#efefef'
221 }
222
223 g
224 end
22585end
toggle raw diff

app/helpers/application_helper.rb

 
7373 def flashes
7474 flash.map {|type, content| content_tag(:div, content_tag(:p, content), :class => "flash_message #{type}")}
7575 end
76
77 def commit_graph_tag(project, sha = "master")
78 repo = project.mainline_repository
79 git_repo = repo.git
80 git = git_repo.git
81
82 h = Hash.new
83 dategroup = Date.new
84
85 data = git.rev_list({:pretty => "format:%aD", :since => "24 weeks ago"}, sha)
86 data.each_line { |line|
87 if line =~ /\d\d:\d\d:\d\d/ then
88 date = Date.parse(line)
89
90 dategroup = Date.new(date.year, date.month, 1)
91 if h[dategroup]
92 h[dategroup] += 1
93 else
94 h[dategroup] = 1
95 end
96 end
97 }
98
99 commits = []
100 labels = []
101 h.sort.each { |entry|
102 date = entry.first
103 value = entry.last
104
105 labels << date.strftime("%m/%y")
106 commits << value
107 }
108
109 Gchart.line(:data => commits, :labels => labels, :bg => "efefef", :format => "img_tag")
110 end
111
112 def commit_graph_by_author_tag(project, sha = "master")
113 repo = project.mainline_repository
114 git_repo = repo.git
115 git = git_repo.git
116
117 h = Hash.new
118
119 data = git.rev_list({:pretty => "format:name:%cn", :since => "1 years ago" }, sha)
120 data.each_line { |line|
121 if line =~ /^name:(.*)$/ then
122 author = $1
123
124 if h[author]
125 h[author] += 1
126 else
127 h[author] = 1
128 end
129 end
130 }
131
132 sorted = h.sort_by { |author, commits|
133 commits
134 }
135
136 labels = []
137 data = []
138
139 max = 5
140 others = []
141 top = sorted
142
143
144 if sorted.size > max
145 top = sorted[sorted.size-max, sorted.size]
146 others = sorted[0, sorted.size-max]
147 end
148
149 top.each { |entry|
150 author = entry.first
151 v = entry.last
152
153 data << v
154 labels << author
155 }
156
157 unless others.empty?
158 others_v = others.inject { |v, acum| [v.last + acum.last] }
159 labels[label_it] = others_v.first
160
161 labels << "others"
162 data << others_v
163 end
164
165 Gchart.pie(:data => data, :labels => labels, :width => 400, :bg => "efefef", :format => "img_tag" )
166 end
76167end
toggle raw diff

app/views/browse/index.html.erb

 
99 <% unless @commits.blank? -%>
1010 <li><strong>HEAD tree:</strong> <%= link_to h(@commits.first.tree.id),
1111 tree_path(@commits.first.id) -%></li>
12 <li><%= image_tag("/projects/commit_graph_by_author/#{@project.slug}?width=400") %></li>
12 <li><%= commit_graph_by_author_tag(@project) %></li>
1313 <% end -%>
1414</ul>
1515
toggle raw diff

app/views/projects/_project.html.erb

 
11<h3><%= link_to project.title, project_path(project) -%></h3>
22<p><%= truncate h(project.stripped_description), 250 -%></p>
33<% if project.repositories.first.has_commits? %>
4 <div><%= image_tag("/projects/commit_graph/#{project.slug}?width=130") %></div>
4 <div><%= commit_graph_tag(project) %></div>
55<% end -%>
66<p class="hint">
77 <strong>Categories:</strong>
toggle raw diff

app/views/projects/show.html.erb

 
2121 <%= link_to base_url(@project.bugtracker_url), h(@project.bugtracker_url) -%></li>
2222 <% end -%>
2323 <% if @repositories.first.has_commits? %>
24 <li><%= image_tag("/projects/commit_graph/#{@project.slug}?width=250") %></li>
24 <li><%= commit_graph_tag(@project) %></li>
2525 <% end -%>
2626 </ul>
2727</div>
toggle raw diff

public/javascripts/prototype.js

 
1/* Prototype JavaScript framework, version 1.6.0.1
2 * (c) 2005-2007 Sam Stephenson
1/* Prototype JavaScript framework, version 1.6.0.2
2 * (c) 2005-2008 Sam Stephenson
33 *
44 * Prototype is freely distributable under the terms of an MIT-style license.
55 * For details, see the Prototype web site: http://www.prototypejs.org/
77 *--------------------------------------------------------------------------*/
88
99var Prototype = {
10 Version: '1.6.0.1',
10 Version: '1.6.0.2',
1111
1212 Browser: {
1313 IE: !!(window.attachEvent && !window.opera),
110110 try {
111111 if (Object.isUndefined(object)) return 'undefined';
112112 if (object === null) return 'null';
113 return object.inspect ? object.inspect() : object.toString();
113 return object.inspect ? object.inspect() : String(object);
114114 } catch (e) {
115115 if (e instanceof RangeError) return '...';
116116 throw e;
171171 },
172172
173173 isArray: function(object) {
174 return object && object.constructor === Array;
174 return object != null && typeof object == "object" &&
175 'splice' in object && 'join' in object;
175176 },
176177
177178 isHash: function(object) {
579579 }
580580
581581 return before + String.interpret(ctx);
582 }.bind(this));
582 });
583583 }
584584});
585585Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
807807function $A(iterable) {
808808 if (!iterable) return [];
809809 if (iterable.toArray) return iterable.toArray();
810 var length = iterable.length, results = new Array(length);
810 var length = iterable.length || 0, results = new Array(length);
811811 while (length--) results[length] = iterable[length];
812812 return results;
813813}
814814
815815if (Prototype.Browser.WebKit) {
816 function $A(iterable) {
816 $A = function(iterable) {
817817 if (!iterable) return [];
818818 if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
819819 iterable.toArray) return iterable.toArray();
820 var length = iterable.length, results = new Array(length);
820 var length = iterable.length || 0, results = new Array(length);
821821 while (length--) results[length] = iterable[length];
822822 return results;
823 }
823 };
824824}
825825
826826Array.from = $A;
12991299
13001300 var contentType = response.getHeader('Content-type');
13011301 if (this.options.evalJS == 'force'
1302 || (this.options.evalJS && contentType
1302 || (this.options.evalJS && this.isSameOrigin() && contentType
13031303 && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
13041304 this.evalResponse();
13051305 }
13171317 }
13181318 },
13191319
1320 isSameOrigin: function() {
1321 var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1322 return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1323 protocol: location.protocol,
1324 domain: document.domain,
1325 port: location.port ? ':' + location.port : ''
1326 }));
1327 },
1328
13201329 getHeader: function(name) {
13211330 try {
1322 return this.transport.getResponseHeader(name);
1331 return this.transport.getResponseHeader(name) || null;
13231332 } catch (e) { return null }
13241333 },
13251334
14011401 if (!json) return null;
14021402 json = decodeURIComponent(escape(json));
14031403 try {
1404 return json.evalJSON(this.request.options.sanitizeJSON);
1404 return json.evalJSON(this.request.options.sanitizeJSON ||
1405 !this.request.isSameOrigin());
14051406 } catch (e) {
14061407 this.request.dispatchException(e);
14071408 }
14151415 this.responseText.blank())
14161416 return null;
14171417 try {
1418 return this.responseText.evalJSON(options.sanitizeJSON);
1418 return this.responseText.evalJSON(options.sanitizeJSON ||
1419 !this.request.isSameOrigin());
14191420 } catch (e) {
14201421 this.request.dispatchException(e);
14211422 }
16201620 Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
16211621 insertions = {bottom:insertions};
16221622
1623 var content, t, range;
1623 var content, insert, tagName, childNodes;
16241624
1625 for (position in insertions) {
1625 for (var position in insertions) {
16261626 content = insertions[position];
16271627 position = position.toLowerCase();
1628 t = Element._insertionTranslations[position];
1628 insert = Element._insertionTranslations[position];
16291629
16301630 if (content && content.toElement) content = content.toElement();
16311631 if (Object.isElement(content)) {
1632 t.insert(element, content);
1632 insert(element, content);
16331633 continue;
16341634 }
16351635
16361636 content = Object.toHTML(content);
16371637
1638 range = element.ownerDocument.createRange();
1639 t.initializeRange(element, range);
1640 t.insert(element, range.createContextualFragment(content.stripScripts()));
1638 tagName = ((position == 'before' || position == 'after')
1639 ? element.parentNode : element).tagName.toUpperCase();
1640
1641 childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
1642
1643 if (position == 'top' || position == 'after') childNodes.reverse();
1644 childNodes.each(insert.curry(element));
16411645
16421646 content.evalScripts.bind(content).defer();
16431647 }
16861686 },
16871687
16881688 descendants: function(element) {
1689 return $(element).getElementsBySelector("*");
1689 return $(element).select("*");
16901690 },
16911691
16921692 firstDescendant: function(element) {
17251725 element = $(element);
17261726 if (arguments.length == 1) return $(element.parentNode);
17271727 var ancestors = element.ancestors();
1728 return expression ? Selector.findElement(ancestors, expression, index) :
1729 ancestors[index || 0];
1728 return Object.isNumber(expression) ? ancestors[expression] :
1729 Selector.findElement(ancestors, expression, index);
17301730 },
17311731
17321732 down: function(element, expression, index) {
17331733 element = $(element);
17341734 if (arguments.length == 1) return element.firstDescendant();
1735 var descendants = element.descendants();
1736 return expression ? Selector.findElement(descendants, expression, index) :
1737 descendants[index || 0];
1735 return Object.isNumber(expression) ? element.descendants()[expression] :
1736 element.select(expression)[index || 0];
17381737 },
17391738
17401739 previous: function(element, expression, index) {
17411740 element = $(element);
17421741 if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
17431742 var previousSiblings = element.previousSiblings();
1744 return expression ? Selector.findElement(previousSiblings, expression, index) :
1745 previousSiblings[index || 0];
1743 return Object.isNumber(expression) ? previousSiblings[expression] :
1744 Selector.findElement(previousSiblings, expression, index);
17461745 },
17471746
17481747 next: function(element, expression, index) {
17491748 element = $(element);
17501749 if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
17511750 var nextSiblings = element.nextSiblings();
1752 return expression ? Selector.findElement(nextSiblings, expression, index) :
1753 nextSiblings[index || 0];
1751 return Object.isNumber(expression) ? nextSiblings[expression] :
1752 Selector.findElement(nextSiblings, expression, index);
17541753 },
17551754
17561755 select: function() {
18751875 do { ancestor = ancestor.parentNode; }
18761876 while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
18771877 }
1878 if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
1878 if (nextAncestor && nextAncestor.sourceIndex)
1879 return (e > a && e < nextAncestor.sourceIndex);
18791880 }
18801881
18811882 while (element = element.parentNode)
20202020 if (element) {
20212021 if (element.tagName == 'BODY') break;
20222022 var p = Element.getStyle(element, 'position');
2023 if (p == 'relative' || p == 'absolute') break;
2023 if (p !== 'static') break;
20242024 }
20252025 } while (element);
20262026 return Element._returnOffset(valueL, valueT);
21692169 }
21702170};
21712171
2172
2173if (!document.createRange || Prototype.Browser.Opera) {
2174 Element.Methods.insert = function(element, insertions) {
2175 element = $(element);
2176
2177 if (Object.isString(insertions) || Object.isNumber(insertions) ||
2178 Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2179 insertions = { bottom: insertions };
2180
2181