updated for redmine 2.0 and added a graph for open/closed bugs in project

This commit is contained in:
unknown 2012-08-01 14:25:16 +02:00
parent b7ef44a274
commit b7d04e7d19
8 changed files with 130 additions and 36 deletions

View File

@ -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)

View 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 %>

View 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
View 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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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'