From b7d04e7d197b6964ab8628ebfd5c2e6469c8083c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 1 Aug 2012 14:25:16 +0200 Subject: [PATCH] updated for redmine 2.0 and added a graph for open/closed bugs in project --- app/controllers/graphs_controller.rb | 102 +++++++++++++++++++++------ app/views/graphs/bug_growth.html.erb | 11 +++ assets/stylesheets/bug_growth.css | 7 ++ config/locales/da.yml | 10 +++ config/locales/en.yml | 2 + config/routes.rb | 26 ++++--- lib/issues_sidebar_graph_hook.rb | 4 ++ routes.rb | 4 -- 8 files changed, 130 insertions(+), 36 deletions(-) create mode 100644 app/views/graphs/bug_growth.html.erb create mode 100644 assets/stylesheets/bug_growth.css create mode 100644 config/locales/da.yml delete mode 100644 routes.rb diff --git a/app/controllers/graphs_controller.rb b/app/controllers/graphs_controller.rb index 728981d..17029ef 100644 --- a/app/controllers/graphs_controller.rb +++ b/app/controllers/graphs_controller.rb @@ -1,27 +1,25 @@ require 'SVG/Graph/TimeSeries' class GraphsController < ApplicationController - unloadable ############################################################################ # 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 :confirm_issues_exist, :only => [:issue_growth] before_filter :find_optional_project, :only => [:issue_growth_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 - ############################################################################ # My Page block graphs ############################################################################ - # Displays a ring of issue assignement changes around the current user def recent_assigned_to_changes_graph # Get the top visible projects by issue count @@ -63,7 +61,6 @@ class GraphsController < ApplicationController ############################################################################ # Graph pages ############################################################################ - # Displays total number of issues over time def issue_growth end @@ -73,12 +70,14 @@ class GraphsController < ApplicationController @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} 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 ############################################################################ - # Displays projects by total issues over time def issue_growth_graph @@ -94,14 +93,14 @@ class GraphsController < ApplicationController :show_data_points => false, :show_data_values => false, :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, :x_label_format => "%Y-%m-%d" }) # Get the top visible projects by 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 << " WHERE (%s)" % Project.allowed_to_condition(User.current, :view_issues) unless @project.nil? @@ -114,7 +113,6 @@ class GraphsController < ApplicationController sql << " LIMIT 6" 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 << " FROM #{Issue.table_name}" 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"] } # Generate the created_on lines - top_projects.each do |project_id, total_count| - counts = issue_counts[project_id].sort { |a,b| a["date"]<=>b["date"] } + top_projects.each do |project_id| + counts = Array(issue_counts[project_id]) created_count = 0 created_on_line = Hash.new - created_on_line[(Date.parse(counts.first["date"])-1).to_s] = 0 - counts.each { |count| created_count += count["issue_count"].to_i; created_on_line[count["date"]] = created_count } + 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"].to_s] = created_count } created_on_line[Date.today.to_s] = created_count graph.add_data({ - :data => created_on_line.sort.flatten, + :data => Array(created_on_line).sort.flatten, :title => Project.find(project_id).to_s }) end @@ -140,7 +138,6 @@ class GraphsController < ApplicationController send_data(graph.burn, :type => "image/svg+xml", :disposition => "inline") end - # Displays issues by creation date, cumulatively def issue_age_graph @@ -156,7 +153,7 @@ class GraphsController < ApplicationController :show_data_points => false, :show_data_values => false, :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, :x_label_format => "%b %d" }) @@ -190,7 +187,56 @@ class GraphsController < ApplicationController send_data(graph.burn, :type => "image/svg+xml", :disposition => "inline") end - # Displays open and total issue counts over time + # 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 + headers["Content-Type"] = "image/svg+xml" + send_data(graph.burn, :type => "image/svg+xml", :disposition => "inline") + end + + # Displays open and total issue counts over time def target_version_graph # Initialize the graph @@ -204,12 +250,11 @@ class GraphsController < ApplicationController :show_data_points => true, :show_data_values => false, :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, :x_label_format => "%b %d" }) - # Group issues 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 @@ -285,7 +330,20 @@ class GraphsController < ApplicationController rescue ActiveRecord::RecordNotFound render_404 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 @project = Project.find(params[:project_id]) unless params[:project_id].blank? deny_access unless User.current.allowed_to?(:view_issues, @project, :global => true) diff --git a/app/views/graphs/bug_growth.html.erb b/app/views/graphs/bug_growth.html.erb new file mode 100644 index 0000000..3a8a010 --- /dev/null +++ b/app/views/graphs/bug_growth.html.erb @@ -0,0 +1,11 @@ +

<%= l(:label_graphs_bug_growth) %>

+<% 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 %> \ No newline at end of file diff --git a/assets/stylesheets/bug_growth.css b/assets/stylesheets/bug_growth.css new file mode 100644 index 0000000..093b8a5 --- /dev/null +++ b/assets/stylesheets/bug_growth.css @@ -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; } \ No newline at end of file diff --git a/config/locales/da.yml b/config/locales/da.yml new file mode 100644 index 0000000..2d655b2 --- /dev/null +++ b/config/locales/da.yml @@ -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. diff --git a/config/locales/en.yml b/config/locales/en.yml index 4eda405..30046c0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3,6 +3,8 @@ en: label_graphs_total_vs_closed_issues: Total issues vs. Closed issues label_graphs_old_issues: Open aging issues 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_assigned_to_status_flow: Recent assignment changes (within 24 hours) warning_no_issues: There are no issues to graph. diff --git a/config/routes.rb b/config/routes.rb index 45d7cf5..af7fddb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,11 +1,17 @@ -ActionController::Routing::Routes.draw do |map| - map.connect 'projects/:project_id/issues/old', :controller => 'graphs', :action => 'old_issues' - map.connect 'issues/old', :controller => 'graphs', :action => 'old_issues' - map.connect ':project_id/issue_age_graph', :controller => 'graphs', :action => 'issue_age_graph' - map.connect 'projects/:project_id/issues/growth', :controller => 'graphs', :action => 'issue_growth' - map.connect 'issues/growth', :controller => 'graphs', :action => 'issue_growth' - map.connect ':project_id/issue_growth_graph', :controller => 'graphs', :action => 'issue_growth_graph' - map.connect 'graphs/recent-status-changes', :controller=>"graphs", :action=>"recent_status_changes_graph" - map.connect 'graphs/recent-assigned-to-changes', :controller=>"graphs", :action=>"recent_assigned_to_changes_graph" - map.connect 'graphs/target-version/:id', :controller=>"graphs", :action=>"target_version_graph" +RedmineApp::Application.routes.draw do + match 'issues/old', :to => 'graphs#old_issues', :via => 'get' + match 'issues/issue_age_graph', :to => 'graphs#issue_age_graph', :via => 'get' + match 'projects/:project_id/issues/old', :to => 'graphs#old_issues', :via => 'get' + match ':project_id/issue_age_graph', :to => 'graphs#issue_age_graph', :via => 'get' + match 'issues/growth', :to => 'graphs#issue_growth', :via => 'get' + match 'issues/issue_growth_graph', :to => 'graphs#issue_growth_graph', :via => 'get' + match 'projects/:project_id/issues/growth', :to => 'graphs#issue_growth', :via => 'get' + match ':project_id/issue_growth_graph', :to => 'graphs#issue_growth_graph', :via => 'get' + 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 diff --git a/lib/issues_sidebar_graph_hook.rb b/lib/issues_sidebar_graph_hook.rb index a4fd3de..cb03541 100644 --- a/lib/issues_sidebar_graph_hook.rb +++ b/lib/issues_sidebar_graph_hook.rb @@ -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 << "
" output << link_to(l(:label_graphs_issue_growth), {:controller => 'graphs', :action => 'issue_growth', :only_path => true}) + output << "
" + output << link_to(l(:label_graphs_bug_growth), {:controller => 'graphs', :action => 'bug_growth', :only_path => true}) output << "
" return output 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 << "
" output << link_to(l(:label_graphs_issue_growth), {:controller => 'graphs', :action => 'issue_growth', :project_id => context[:project], :only_path => true}) + output << "
" + output << link_to(l(:label_graphs_bug_growth), {:controller => 'graphs', :action => 'bug_growth', :project_id => context[:project], :only_path => true}) output << "
" return output end diff --git a/routes.rb b/routes.rb deleted file mode 100644 index 6544e8f..0000000 --- a/routes.rb +++ /dev/null @@ -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' \ No newline at end of file