diff --git a/app/controllers/graphs_controller.rb b/app/controllers/graphs_controller.rb index 588a180..c6d0755 100755 --- a/app/controllers/graphs_controller.rb +++ b/app/controllers/graphs_controller.rb @@ -2,17 +2,73 @@ require 'SVG/Graph/TimeSeries' class GraphsController < ApplicationController + before_filter :find_version, :only => [:target_version_graph] + before_filter :find_issues, :only => [:old_issues, :issue_age_graph] + + helper IssuesHelper + + def old_issues + @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 - before_filter :find_version, :only => [:target_version] + # Displays issues by creation date, cumulatively + def issue_age_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 => true, + :show_data_values => false, + :stagger_x_labels => true, + :style_sheet => "/plugin_assets/redmine_graphs/stylesheets/issue_age.css", + :timescale_divisions => "1 weeks", + :width => 800, + :x_label_format => "%b %d" + }) - - def target_version + # Group issues + issues_by_created_on = @issues.group_by {|issue| issue.created_on.to_date }.sort + issues_by_updated_on = @issues.group_by {|issue| issue.updated_on.to_date }.sort + + # Generate the created_on line + created_count = 0 + created_on_line = Hash.new + issues_by_created_on.each { |created_on, issues| created_on_line[(created_on-1).to_s] = created_count; created_count += issues.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) + }) + + # Generate the closed_on line + updated_count = 0 + updated_on_line = Hash.new + issues_by_updated_on.each { |updated_on, issues| updated_on_line[(updated_on-1).to_s] = updated_count; updated_count += issues.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(:field_updated_on) + }) + + # 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 graph = SVG::Graph::TimeSeries.new({ :area_fill => true, :height => 300, - :key => true, :no_css => true, :show_x_guidelines => true, :scale_x_integers => true, @@ -26,12 +82,12 @@ class GraphsController < ApplicationController }) # Group issues - issues_by_created_on = @version.fixed_issues.group_by {|issue| issue.created_on.to_date } - issues_by_updated_on = @version.fixed_issues.group_by {|issue| issue.updated_on.to_date } - issues_by_closed_on = @version.fixed_issues.collect { |issue| issue if issue.closed? }.compact.group_by {|issue| issue.updated_on.to_date } + 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_closed_on = @version.fixed_issues.collect { |issue| issue if issue.closed? }.compact.group_by {|issue| issue.updated_on.to_date }.sort # Set the scope of the graph - scope_end_date = issues_by_updated_on.sort.keys.last + scope_end_date = issues_by_updated_on.keys.last scope_end_date = @version.effective_date if !@version.effective_date.nil? && @version.effective_date > scope_end_date scope_end_date = Date.today if !@version.completed? line_end_date = Date.today @@ -40,7 +96,7 @@ class GraphsController < ApplicationController # Generate the created_on line created_count = 0 created_on_line = Hash.new - issues_by_created_on.sort.each { |created_on, issues| created_on_line[(created_on-1).to_s] = created_count; created_count += issues.size; created_on_line[created_on.to_s] = created_count } + issues_by_created_on.each { |created_on, issues| created_on_line[(created_on-1).to_s] = created_count; created_count += issues.size; created_on_line[created_on.to_s] = created_count } created_on_line[scope_end_date.to_s] = created_count graph.add_data({ :data => created_on_line.sort.flatten, @@ -50,7 +106,7 @@ class GraphsController < ApplicationController # Generate the closed_on line closed_count = 0 closed_on_line = Hash.new - issues_by_closed_on.sort.each { |closed_on, issues| closed_on_line[(closed_on-1).to_s] = closed_count; closed_count += issues.size; closed_on_line[closed_on.to_s] = closed_count } + issues_by_closed_on.each { |closed_on, issues| closed_on_line[(closed_on-1).to_s] = closed_count; closed_count += issues.size; closed_on_line[closed_on.to_s] = closed_count } closed_on_line[line_end_date.to_s] = closed_count graph.add_data({ :data => closed_on_line.sort.flatten, @@ -72,6 +128,14 @@ class GraphsController < ApplicationController private + def find_issues + @project = Project.find(params[:project_id]) unless params[:project_id].blank? + deny_access unless User.current.allowed_to?(:view_issues, @project, :global => true) + @issues = Issue.visible.find(:all, :include => [:status], :conditions => ["#{IssueStatus.table_name}.is_closed=?", false]) if @project.nil? + @issues = @project.issues.collect { |issue| issue unless issue.closed? }.compact unless @project.nil? + rescue ActiveRecord::RecordNotFound + render_404 + end def find_version @version = Version.find(params[:id]) diff --git a/app/views/graphs/old_issues.html.erb b/app/views/graphs/old_issues.html.erb new file mode 100755 index 0000000..359ed27 --- /dev/null +++ b/app/views/graphs/old_issues.html.erb @@ -0,0 +1,21 @@ +

<%= l(:label_graphs_old_issues) %>

+<%= tag("embed", :width => "100%", :height => 300, :type => "image/svg+xml", :src => url_for(:controller => 'graphs', :action => 'issue_age_graph')) if @project.nil? %> +<%= tag("embed", :width => "100%", :height => 300, :type => "image/svg+xml", :src => url_for(:controller => 'graphs', :action => 'issue_age_graph', :project_id => @project.id)) unless @project.nil? %> + +
+
+

<%= l(:label_issues_by, :value => l(:field_created_on)) %>

+ <%= render :partial => 'issues/list_simple', :locals => { :issues => @issues_by_created_on[0..9] } %> +
+
+ +
+
+

<%= l(:label_issues_by, :value => l(:field_updated_on)) %>

+ <%= render :partial => 'issues/list_simple', :locals => { :issues => @issues_by_updated_on[0..9] } %> +
+
+ +<% content_for :sidebar do %> + <%= render :partial => 'issues/sidebar' %> +<% end %> \ No newline at end of file diff --git a/assets/stylesheets/issue_age.css b/assets/stylesheets/issue_age.css new file mode 100755 index 0000000..17f28d6 --- /dev/null +++ b/assets/stylesheets/issue_age.css @@ -0,0 +1,7 @@ +.fill1, .key1, .dataPoint1 { fill: #666 !important; } +.line1 { stroke: #666 !important; } + +.fill2, .key2 { fill: #bac9e0 !important; } +.fill2 { fill-opacity: 0.8 !important; } +.line2 { stroke: #003d99 !important; } +.dataPoint2 { fill: #003d99 !important; } \ No newline at end of file diff --git a/assets/stylesheets/target_version.css b/assets/stylesheets/target_version.css index 92c071e..a4040f7 100755 --- a/assets/stylesheets/target_version.css +++ b/assets/stylesheets/target_version.css @@ -1,10 +1,10 @@ -.fill1, .key1, .dataPoint1 { fill: #666666 !important; } -.line1 { stroke: #666666 !important; } +.fill1, .key1, .dataPoint1 { fill: #666 !important; } +.line1 { stroke: #666 !important; } .fill2, .key2 { fill: #BAE0BA !important; } .fill2 { fill-opacity: 0.8 !important; } -.line2 { stroke: #009900 !important; } -.dataPoint2 { fill: #009900 !important; } +.line2 { stroke: #090 !important; } +.dataPoint2 { fill: #090 !important; } .key3 { fill: #95964e !important; } .dataPoint3 { stroke: #95964e !important; stroke-width: 10px !important; } \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index a3b8fac..4d84ca6 100755 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,2 +1,4 @@ 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: Old issues + \ No newline at end of file diff --git a/init.rb b/init.rb index a7bcd6a..91a2468 100755 --- a/init.rb +++ b/init.rb @@ -1,6 +1,7 @@ require 'redmine' require_dependency 'target_version_graph_hook' +require_dependency 'old_issues_graph_hook' Redmine::Plugin.register :redmine_graphs do name 'Redmine Graphs plugin' diff --git a/lib/old_issues_graph_hook.rb b/lib/old_issues_graph_hook.rb new file mode 100755 index 0000000..4c10732 --- /dev/null +++ b/lib/old_issues_graph_hook.rb @@ -0,0 +1,9 @@ +# Provides a link to the issue age graph on the issue index page +class OldIssuesGraphHook < Redmine::Hook::ViewListener + def view_issues_sidebar_issues_bottom(context = { }) + output = link_to l(:label_graphs_old_issues), :controller => 'graphs', :action => 'old_issues' if context[:project].nil? + output = link_to l(:label_graphs_old_issues), :controller => 'graphs', :action => 'old_issues', :project_id => context[:project] unless context[:project].nil? + output << "
" + return output + end +end \ No newline at end of file diff --git a/lib/target_version_graph_hook.rb b/lib/target_version_graph_hook.rb index ed35cde..5b4e6b1 100755 --- a/lib/target_version_graph_hook.rb +++ b/lib/target_version_graph_hook.rb @@ -3,7 +3,7 @@ class TargetVersionGraphHook < Redmine::Hook::ViewListener def view_versions_show_bottom(context = { }) if !context[:version].fixed_issues.empty? output = "
#{ l(:label_graphs_total_vs_closed_issues) }" - output << tag("embed", :width => "100%", :height => 300, :type => "image/svg+xml", :src => url_for(:controller => 'graphs', :action => 'target_version', :id => context[:version])) + output << tag("embed", :width => "100%", :height => 300, :type => "image/svg+xml", :src => url_for(:controller => 'graphs', :action => 'target_version_graph', :id => context[:version])) output << "
" return output end diff --git a/routes.rb b/routes.rb new file mode 100755 index 0000000..5e2e7e0 --- /dev/null +++ b/routes.rb @@ -0,0 +1,2 @@ +map.connect 'projects/:project_id/issues/old', :controller => 'graphs', :action => 'old_issues' +map.connect 'issues/old', :controller => 'graphs', :action => 'old_issues' \ No newline at end of file