updated for redmine 2.0 and added a graph for open/closed bugs in project
This commit is contained in:
parent
b7ef44a274
commit
b7d04e7d19
|
@ -1,27 +1,25 @@
|
||||||
require 'SVG/Graph/TimeSeries'
|
require 'SVG/Graph/TimeSeries'
|
||||||
|
|
||||||
class GraphsController < ApplicationController
|
class GraphsController < ApplicationController
|
||||||
|
|
||||||
unloadable
|
unloadable
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# Initialization
|
# Initialization
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
menu_item :issues, :only => [:issue_growth, :old_issues]
|
menu_item :issues, :only => [:issue_growth, :old_issues, :bug_growth]
|
||||||
|
|
||||||
before_filter :find_version, :only => [:target_version_graph]
|
before_filter :find_version, :only => [:target_version_graph]
|
||||||
before_filter :confirm_issues_exist, :only => [:issue_growth]
|
before_filter :confirm_issues_exist, :only => [:issue_growth]
|
||||||
before_filter :find_optional_project, :only => [:issue_growth_graph]
|
before_filter :find_optional_project, :only => [:issue_growth_graph]
|
||||||
before_filter :find_open_issues, :only => [:old_issues, :issue_age_graph]
|
before_filter :find_open_issues, :only => [:old_issues, :issue_age_graph]
|
||||||
|
before_filter :find_bug_issues, :only => [:bug_growth, :bug_growth_graph]
|
||||||
|
|
||||||
helper IssuesHelper
|
helper IssuesHelper
|
||||||
|
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# My Page block graphs
|
# My Page block graphs
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
# Displays a ring of issue assignement changes around the current user
|
# Displays a ring of issue assignement changes around the current user
|
||||||
def recent_assigned_to_changes_graph
|
def recent_assigned_to_changes_graph
|
||||||
# Get the top visible projects by issue count
|
# Get the top visible projects by issue count
|
||||||
|
@ -63,7 +61,6 @@ class GraphsController < ApplicationController
|
||||||
############################################################################
|
############################################################################
|
||||||
# Graph pages
|
# Graph pages
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
# Displays total number of issues over time
|
# Displays total number of issues over time
|
||||||
def issue_growth
|
def issue_growth
|
||||||
end
|
end
|
||||||
|
@ -73,12 +70,14 @@ class GraphsController < ApplicationController
|
||||||
@issues_by_created_on = @issues.sort {|a,b| a.created_on<=>b.created_on}
|
@issues_by_created_on = @issues.sort {|a,b| a.created_on<=>b.created_on}
|
||||||
@issues_by_updated_on = @issues.sort {|a,b| a.updated_on<=>b.updated_on}
|
@issues_by_updated_on = @issues.sort {|a,b| a.updated_on<=>b.updated_on}
|
||||||
end
|
end
|
||||||
|
# Displays created vs update date on bugs over time
|
||||||
|
def bug_growth
|
||||||
|
@bug_by_created = @bugs.group_by {|issue| issue.created_on.to_date }.sort
|
||||||
|
end
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# Embedded graphs for graph pages
|
# Embedded graphs for graph pages
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
# Displays projects by total issues over time
|
# Displays projects by total issues over time
|
||||||
def issue_growth_graph
|
def issue_growth_graph
|
||||||
|
|
||||||
|
@ -94,14 +93,14 @@ class GraphsController < ApplicationController
|
||||||
:show_data_points => false,
|
:show_data_points => false,
|
||||||
:show_data_values => false,
|
:show_data_values => false,
|
||||||
:stagger_x_labels => true,
|
:stagger_x_labels => true,
|
||||||
:style_sheet => "/plugin_assets/redmine-graphs-plugin/stylesheets/issue_growth.css",
|
:style_sheet => "/plugin_assets/redmine_graphs/stylesheets/issue_growth.css",
|
||||||
:width => 720,
|
:width => 720,
|
||||||
:x_label_format => "%Y-%m-%d"
|
:x_label_format => "%Y-%m-%d"
|
||||||
})
|
})
|
||||||
|
|
||||||
# Get the top visible projects by issue count
|
# Get the top visible projects by issue count
|
||||||
sql = "SELECT project_id, COUNT(*) as issue_count"
|
sql = "SELECT project_id, COUNT(*) as issue_count"
|
||||||
sql << " FROM issues"
|
sql << " FROM #{Issue.table_name}"
|
||||||
sql << " LEFT JOIN #{Project.table_name} ON #{Issue.table_name}.project_id = #{Project.table_name}.id"
|
sql << " LEFT JOIN #{Project.table_name} ON #{Issue.table_name}.project_id = #{Project.table_name}.id"
|
||||||
sql << " WHERE (%s)" % Project.allowed_to_condition(User.current, :view_issues)
|
sql << " WHERE (%s)" % Project.allowed_to_condition(User.current, :view_issues)
|
||||||
unless @project.nil?
|
unless @project.nil?
|
||||||
|
@ -114,7 +113,6 @@ class GraphsController < ApplicationController
|
||||||
sql << " LIMIT 6"
|
sql << " LIMIT 6"
|
||||||
top_projects = ActiveRecord::Base.connection.select_all(sql).collect { |p| p["project_id"] }
|
top_projects = ActiveRecord::Base.connection.select_all(sql).collect { |p| p["project_id"] }
|
||||||
|
|
||||||
# Get the issues created per project, per day
|
|
||||||
sql = "SELECT project_id, date(#{Issue.table_name}.created_on) as date, COUNT(*) as issue_count"
|
sql = "SELECT project_id, date(#{Issue.table_name}.created_on) as date, COUNT(*) as issue_count"
|
||||||
sql << " FROM #{Issue.table_name}"
|
sql << " FROM #{Issue.table_name}"
|
||||||
sql << " WHERE project_id IN (%s)" % top_projects.compact.join(',')
|
sql << " WHERE project_id IN (%s)" % top_projects.compact.join(',')
|
||||||
|
@ -122,15 +120,15 @@ class GraphsController < ApplicationController
|
||||||
issue_counts = ActiveRecord::Base.connection.select_all(sql).group_by { |c| c["project_id"] }
|
issue_counts = ActiveRecord::Base.connection.select_all(sql).group_by { |c| c["project_id"] }
|
||||||
|
|
||||||
# Generate the created_on lines
|
# Generate the created_on lines
|
||||||
top_projects.each do |project_id, total_count|
|
top_projects.each do |project_id|
|
||||||
counts = issue_counts[project_id].sort { |a,b| a["date"]<=>b["date"] }
|
counts = Array(issue_counts[project_id])
|
||||||
created_count = 0
|
created_count = 0
|
||||||
created_on_line = Hash.new
|
created_on_line = Hash.new
|
||||||
created_on_line[(Date.parse(counts.first["date"])-1).to_s] = 0
|
created_on_line[(Date.parse( Array(counts).first["date"].to_s )-1).to_s] = 0
|
||||||
counts.each { |count| created_count += count["issue_count"].to_i; created_on_line[count["date"]] = created_count }
|
counts.each { |count| created_count += count["issue_count"].to_i; created_on_line[count["date"].to_s] = created_count }
|
||||||
created_on_line[Date.today.to_s] = created_count
|
created_on_line[Date.today.to_s] = created_count
|
||||||
graph.add_data({
|
graph.add_data({
|
||||||
:data => created_on_line.sort.flatten,
|
:data => Array(created_on_line).sort.flatten,
|
||||||
:title => Project.find(project_id).to_s
|
:title => Project.find(project_id).to_s
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -140,7 +138,6 @@ class GraphsController < ApplicationController
|
||||||
send_data(graph.burn, :type => "image/svg+xml", :disposition => "inline")
|
send_data(graph.burn, :type => "image/svg+xml", :disposition => "inline")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Displays issues by creation date, cumulatively
|
# Displays issues by creation date, cumulatively
|
||||||
def issue_age_graph
|
def issue_age_graph
|
||||||
|
|
||||||
|
@ -156,7 +153,7 @@ class GraphsController < ApplicationController
|
||||||
:show_data_points => false,
|
:show_data_points => false,
|
||||||
:show_data_values => false,
|
:show_data_values => false,
|
||||||
:stagger_x_labels => true,
|
:stagger_x_labels => true,
|
||||||
:style_sheet => "/plugin_assets/redmine-graphs-plugin/stylesheets/issue_age.css",
|
:style_sheet => "/plugin_assets/redmine_graphs/stylesheets/issue_age.css",
|
||||||
:width => 720,
|
:width => 720,
|
||||||
:x_label_format => "%b %d"
|
:x_label_format => "%b %d"
|
||||||
})
|
})
|
||||||
|
@ -185,6 +182,55 @@ class GraphsController < ApplicationController
|
||||||
:title => l(:field_updated_on)
|
:title => l(:field_updated_on)
|
||||||
}) unless issues_by_updated_on.empty?
|
}) unless issues_by_updated_on.empty?
|
||||||
|
|
||||||
|
# Compile the graph
|
||||||
|
headers["Content-Type"] = "image/svg+xml"
|
||||||
|
send_data(graph.burn, :type => "image/svg+xml", :disposition => "inline")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Displays bugs over time
|
||||||
|
def bug_growth_graph
|
||||||
|
|
||||||
|
# Initialize the graph
|
||||||
|
graph = SVG::Graph::TimeSeries.new({
|
||||||
|
:area_fill => true,
|
||||||
|
:height => 300,
|
||||||
|
:min_y_value => 0,
|
||||||
|
:no_css => true,
|
||||||
|
:show_x_guidelines => true,
|
||||||
|
:scale_x_integers => true,
|
||||||
|
:scale_y_integers => true,
|
||||||
|
:show_data_points => false,
|
||||||
|
:show_data_values => false,
|
||||||
|
:stagger_x_labels => true,
|
||||||
|
:style_sheet => "/plugin_assets/redmine_graphs/stylesheets/bug_growth.css",
|
||||||
|
:width => 720,
|
||||||
|
:x_label_format => "%Y-%m-%d"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Group issues
|
||||||
|
bug_by_created_on = @bugs.group_by {|issue| issue.created_on.to_date }.sort
|
||||||
|
bug_by_updated_on = @bugs.delete_if {|issue| !issue.closed? }.group_by {|issue| issue.updated_on.to_date }.sort
|
||||||
|
|
||||||
|
# Generate the created_on line
|
||||||
|
created_count = 0
|
||||||
|
created_on_line = Hash.new
|
||||||
|
bug_by_created_on.each { |created_on, bugs| created_on_line[(created_on-1).to_s] = created_count; created_count += bugs.size; created_on_line[created_on.to_s] = created_count }
|
||||||
|
created_on_line[Date.today.to_s] = created_count
|
||||||
|
graph.add_data({
|
||||||
|
:data => created_on_line.sort.flatten,
|
||||||
|
:title => l(:field_created_on)
|
||||||
|
}) unless bug_by_created_on.empty?
|
||||||
|
|
||||||
|
# Generate the closed_on line
|
||||||
|
updated_count = 0
|
||||||
|
updated_on_line = Hash.new
|
||||||
|
bug_by_updated_on.each { |updated_on, bugs| updated_on_line[(updated_on-1).to_s] = updated_count; updated_count += bugs.size; updated_on_line[updated_on.to_s] = updated_count }
|
||||||
|
updated_on_line[Date.today.to_s] = updated_count
|
||||||
|
graph.add_data({
|
||||||
|
:data => updated_on_line.sort.flatten,
|
||||||
|
:title => l(:label_graphs_closed_bugs)
|
||||||
|
}) unless bug_by_updated_on.empty?
|
||||||
|
|
||||||
# Compile the graph
|
# Compile the graph
|
||||||
headers["Content-Type"] = "image/svg+xml"
|
headers["Content-Type"] = "image/svg+xml"
|
||||||
send_data(graph.burn, :type => "image/svg+xml", :disposition => "inline")
|
send_data(graph.burn, :type => "image/svg+xml", :disposition => "inline")
|
||||||
|
@ -204,12 +250,11 @@ class GraphsController < ApplicationController
|
||||||
:show_data_points => true,
|
:show_data_points => true,
|
||||||
:show_data_values => false,
|
:show_data_values => false,
|
||||||
:stagger_x_labels => true,
|
:stagger_x_labels => true,
|
||||||
:style_sheet => "/plugin_assets/redmine-graphs-plugin/stylesheets/target_version.css",
|
:style_sheet => "/plugin_assets/redmine_graphs/stylesheets/target_version.css",
|
||||||
:width => 800,
|
:width => 800,
|
||||||
:x_label_format => "%b %d"
|
:x_label_format => "%b %d"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
# Group issues
|
# Group issues
|
||||||
issues_by_created_on = @version.fixed_issues.group_by {|issue| issue.created_on.to_date }.sort
|
issues_by_created_on = @version.fixed_issues.group_by {|issue| issue.created_on.to_date }.sort
|
||||||
issues_by_updated_on = @version.fixed_issues.group_by {|issue| issue.updated_on.to_date }.sort
|
issues_by_updated_on = @version.fixed_issues.group_by {|issue| issue.updated_on.to_date }.sort
|
||||||
|
@ -286,6 +331,19 @@ class GraphsController < ApplicationController
|
||||||
render_404
|
render_404
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_bug_issues
|
||||||
|
find_optional_project
|
||||||
|
if !@project.nil?
|
||||||
|
ids = [@project.id]
|
||||||
|
ids += @project.descendants.active.visible.collect(&:id)
|
||||||
|
@bugs= Issue.visible.find(:all, :include => [:status], :conditions => ["#{Issue.table_name}.tracker_id IN (?) AND #{Project.table_name}.id IN (?)", 1, ids])
|
||||||
|
else
|
||||||
|
@bugs= Issue.visible.find(:all, :include => [:status], :conditions => ["#{Issue.table_name}.tracker_id IN (?)", 1])
|
||||||
|
end
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
|
||||||
def find_optional_project
|
def find_optional_project
|
||||||
@project = Project.find(params[:project_id]) unless params[:project_id].blank?
|
@project = Project.find(params[:project_id]) unless params[:project_id].blank?
|
||||||
deny_access unless User.current.allowed_to?(:view_issues, @project, :global => true)
|
deny_access unless User.current.allowed_to?(:view_issues, @project, :global => true)
|
||||||
|
|
11
app/views/graphs/bug_growth.html.erb
Normal file
11
app/views/graphs/bug_growth.html.erb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<h2><%= l(:label_graphs_bug_growth) %></h2>
|
||||||
|
<% unless @bugs.empty? %>
|
||||||
|
<%= tag("embed", :width => "100%", :height => 300, :type => "image/svg+xml", :src => url_for(:controller => 'graphs', :action => 'bug_growth_graph')) if @project.nil? %>
|
||||||
|
<%= tag("embed", :width => "100%", :height => 300, :type => "image/svg+xml", :src => url_for(:controller => 'graphs', :action => 'bug_growth_graph', :project_id => @project.id)) unless @project.nil? %>
|
||||||
|
<% else %>
|
||||||
|
<%= render :partial => 'issues/list_simple', :locals => { :issues => @issues } %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% content_for :sidebar do %>
|
||||||
|
<%= render :partial => 'issues/sidebar' %>
|
||||||
|
<% end %>
|
7
assets/stylesheets/bug_growth.css
Normal file
7
assets/stylesheets/bug_growth.css
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.line1 { stroke: #666666 !important; } .fill1 { fill: #666666 !important; } .key1 { fill: #666666 !important; }
|
||||||
|
.line2 { stroke: #507AAA !important; } .fill2 { fill: #BACCE0 !important; } .key2 { fill: #507AAA !important; }
|
||||||
|
|
||||||
|
.fill1, .fill2 { fill-opacity: 0.6 !important; }
|
||||||
|
.line1, .line2 { stroke-width: 2px !important; }
|
||||||
|
|
||||||
|
.guideLines { stroke-opacity: 0.2 !important; }
|
10
config/locales/da.yml
Normal file
10
config/locales/da.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
da:
|
||||||
|
label_graphs: Grafer
|
||||||
|
label_graphs_total_vs_closed_issues: Totale antal sager vs. lukkede sager
|
||||||
|
label_graphs_old_issues: Åbne aldrende sager
|
||||||
|
label_graphs_issue_growth: Totale antal sager over tid
|
||||||
|
label_graphs_bug_growth: Totale antal bugs over tid
|
||||||
|
label_graphs_closed_bugs: Lukket
|
||||||
|
label_graphs_issue_status_flow: Seneste statusændringer (inden for 24 timer)
|
||||||
|
label_graphs_assigned_to_status_flow: Seneste tildelingsændringer (inden for 24 timer)
|
||||||
|
warning_no_issues: Der er ingen sager til grafen.
|
|
@ -3,6 +3,8 @@ en:
|
||||||
label_graphs_total_vs_closed_issues: Total issues vs. Closed issues
|
label_graphs_total_vs_closed_issues: Total issues vs. Closed issues
|
||||||
label_graphs_old_issues: Open aging issues
|
label_graphs_old_issues: Open aging issues
|
||||||
label_graphs_issue_growth: Total issues over time
|
label_graphs_issue_growth: Total issues over time
|
||||||
|
label_graphs_bug_growth: Total bugs over time
|
||||||
|
label_graphs_closed_bugs: Closed
|
||||||
label_graphs_issue_status_flow: Recent status changes (within 24 hours)
|
label_graphs_issue_status_flow: Recent status changes (within 24 hours)
|
||||||
label_graphs_assigned_to_status_flow: Recent assignment changes (within 24 hours)
|
label_graphs_assigned_to_status_flow: Recent assignment changes (within 24 hours)
|
||||||
warning_no_issues: There are no issues to graph.
|
warning_no_issues: There are no issues to graph.
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
ActionController::Routing::Routes.draw do |map|
|
RedmineApp::Application.routes.draw do
|
||||||
map.connect 'projects/:project_id/issues/old', :controller => 'graphs', :action => 'old_issues'
|
match 'issues/old', :to => 'graphs#old_issues', :via => 'get'
|
||||||
map.connect 'issues/old', :controller => 'graphs', :action => 'old_issues'
|
match 'issues/issue_age_graph', :to => 'graphs#issue_age_graph', :via => 'get'
|
||||||
map.connect ':project_id/issue_age_graph', :controller => 'graphs', :action => 'issue_age_graph'
|
match 'projects/:project_id/issues/old', :to => 'graphs#old_issues', :via => 'get'
|
||||||
map.connect 'projects/:project_id/issues/growth', :controller => 'graphs', :action => 'issue_growth'
|
match ':project_id/issue_age_graph', :to => 'graphs#issue_age_graph', :via => 'get'
|
||||||
map.connect 'issues/growth', :controller => 'graphs', :action => 'issue_growth'
|
match 'issues/growth', :to => 'graphs#issue_growth', :via => 'get'
|
||||||
map.connect ':project_id/issue_growth_graph', :controller => 'graphs', :action => 'issue_growth_graph'
|
match 'issues/issue_growth_graph', :to => 'graphs#issue_growth_graph', :via => 'get'
|
||||||
map.connect 'graphs/recent-status-changes', :controller=>"graphs", :action=>"recent_status_changes_graph"
|
match 'projects/:project_id/issues/growth', :to => 'graphs#issue_growth', :via => 'get'
|
||||||
map.connect 'graphs/recent-assigned-to-changes', :controller=>"graphs", :action=>"recent_assigned_to_changes_graph"
|
match ':project_id/issue_growth_graph', :to => 'graphs#issue_growth_graph', :via => 'get'
|
||||||
map.connect 'graphs/target-version/:id', :controller=>"graphs", :action=>"target_version_graph"
|
match 'issues/bug_growth', :to => 'graphs#bug_growth', :via => 'get'
|
||||||
|
match 'issues/bug_growth_graph', :to => 'graphs#bug_growth_graph', :via => 'get'
|
||||||
|
match 'projects/:project_id/issues/bug_growth', :to => 'graphs#bug_growth', :via => 'get'
|
||||||
|
match ':project_id/bug_growth_graph', :to => 'graphs#bug_growth_graph', :via => 'get'
|
||||||
|
match 'graphs/recent-status-changes', :to => 'graphs#recent_status_changes_graph', :via => 'get'
|
||||||
|
match 'graphs/recent-assigned-to-changes', :to => 'graphs#recent_assigned_to_changes_graph', :via => 'get'
|
||||||
|
match 'graphs/target-version/:id', :to => 'graphs#target_version_graph', :via => 'get'
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,8 @@ class IssuesSidebarGraphHook < Redmine::Hook::ViewListener
|
||||||
output << link_to(l(:label_graphs_old_issues), {:controller => 'graphs', :action => 'old_issues', :only_path => true})
|
output << link_to(l(:label_graphs_old_issues), {:controller => 'graphs', :action => 'old_issues', :only_path => true})
|
||||||
output << "<br/>"
|
output << "<br/>"
|
||||||
output << link_to(l(:label_graphs_issue_growth), {:controller => 'graphs', :action => 'issue_growth', :only_path => true})
|
output << link_to(l(:label_graphs_issue_growth), {:controller => 'graphs', :action => 'issue_growth', :only_path => true})
|
||||||
|
output << "<br/>"
|
||||||
|
output << link_to(l(:label_graphs_bug_growth), {:controller => 'graphs', :action => 'bug_growth', :only_path => true})
|
||||||
output << "<br/>"
|
output << "<br/>"
|
||||||
return output
|
return output
|
||||||
elsif !context[:project].nil?
|
elsif !context[:project].nil?
|
||||||
|
@ -13,6 +15,8 @@ class IssuesSidebarGraphHook < Redmine::Hook::ViewListener
|
||||||
output << link_to(l(:label_graphs_old_issues), {:controller => 'graphs', :action => 'old_issues', :project_id => context[:project], :only_path => true})
|
output << link_to(l(:label_graphs_old_issues), {:controller => 'graphs', :action => 'old_issues', :project_id => context[:project], :only_path => true})
|
||||||
output << "<br/>"
|
output << "<br/>"
|
||||||
output << link_to(l(:label_graphs_issue_growth), {:controller => 'graphs', :action => 'issue_growth', :project_id => context[:project], :only_path => true})
|
output << link_to(l(:label_graphs_issue_growth), {:controller => 'graphs', :action => 'issue_growth', :project_id => context[:project], :only_path => true})
|
||||||
|
output << "<br/>"
|
||||||
|
output << link_to(l(:label_graphs_bug_growth), {:controller => 'graphs', :action => 'bug_growth', :project_id => context[:project], :only_path => true})
|
||||||
output << "<br/>"
|
output << "<br/>"
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
map.connect 'projects/:project_id/issues/old', :controller => 'graphs', :action => 'old_issues'
|
|
||||||
map.connect 'issues/old', :controller => 'graphs', :action => 'old_issues'
|
|
||||||
map.connect 'projects/:project_id/issues/growth', :controller => 'graphs', :action => 'issue_growth'
|
|
||||||
map.connect 'issues/growth', :controller => 'graphs', :action => 'issue_growth'
|
|
Loading…
Reference in New Issue
Block a user