diff --git a/app/controllers/graphs_controller.rb b/app/controllers/graphs_controller.rb index 359c453..792e7cc 100755 --- a/app/controllers/graphs_controller.rb +++ b/app/controllers/graphs_controller.rb @@ -4,7 +4,7 @@ class GraphsController < ApplicationController before_filter :find_version, :only => [:target_version_graph] before_filter :find_open_issues, :only => [:old_issues, :issue_age_graph] - before_filter :find_all_issues, :only => [:issue_growth_graph, :issue_growth] + before_filter :find_optional_project, :only => [:issue_growth_graph] helper IssuesHelper @@ -16,6 +16,7 @@ class GraphsController < ApplicationController # Initialize the graph graph = SVG::Graph::TimeSeries.new({ + :area_fill => true, :height => 300, :min_y_value => 0, :no_css => true, @@ -26,27 +27,41 @@ class GraphsController < ApplicationController :show_data_values => false, :stagger_x_labels => true, :style_sheet => "/plugin_assets/redmine_graphs/stylesheets/issue_growth.css", - :timescale_divisions => "1 weeks", - :width => 800, - :x_label_format => "%b %d" + :timescale_divisions => "1 months", + :width => 720, + :x_label_format => "%b %y" }) - - # Group issues - issues_by_project = @issues.group_by {|issue| issue.project } - projects_by_size = issues_by_project.collect { |project, issues| [project, issues.size] }.sort { |a,b| b[1]<=>a[1] }[0..5] + + # Get the top visible projects by issue count + sql = "SELECT project_id, COUNT(*) issue_count" + sql << " FROM issues" + 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 << " GROUP BY project_id" + sql << " ORDER BY issue_count DESC" + sql << " LIMIT 6" + top_projects = ActiveRecord::Base.connection.select_all(sql).collect { |p| p["project_id"] } - # Generate the created_on line - projects_by_size.each do |project, size| - issues_by_created_on = issues_by_project[project].group_by {|issue| issue.created_on.to_date }.sort + # 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(',') + sql << " AND (project_id = #{@project.id})" unless @project.nil? + sql << " GROUP BY project_id, date" + 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"] } created_count = 0 created_on_line = Hash.new - created_on_line[(issues_by_created_on.first[0]-1).to_s] = 0 - issues_by_created_on.each { |created_on, issues| created_count += issues.size; created_on_line[created_on.to_s] = created_count } + 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.today.to_s] = created_count graph.add_data({ :data => created_on_line.sort.flatten, - :title => project.name - }) + :title => Project.find(project_id).to_s + }) end # Compile the graph @@ -71,12 +86,12 @@ class GraphsController < ApplicationController :show_x_guidelines => true, :scale_x_integers => true, :scale_y_integers => true, - :show_data_points => true, + :show_data_points => false, :show_data_values => false, :stagger_x_labels => true, :style_sheet => "/plugin_assets/redmine_graphs/stylesheets/issue_age.css", :timescale_divisions => "1 weeks", - :width => 800, + :width => 720, :x_label_format => "%b %d" }) @@ -176,19 +191,16 @@ class GraphsController < ApplicationController private def find_open_issues - @project = Project.find(params[:project_id]) unless params[:project_id].blank? - deny_access unless User.current.allowed_to?(:view_issues, @project, :global => true) + find_optional_project @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_all_issues + 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) if @project.nil? - @issues = Issue.visible.find(:all, :include => [:project]) - @issues = @project.issues unless @project.nil? + deny_access unless User.current.allowed_to?(:view_issues, @project, :global => true) rescue ActiveRecord::RecordNotFound render_404 end @@ -199,4 +211,4 @@ class GraphsController < ApplicationController rescue ActiveRecord::RecordNotFound render_404 end -end \ No newline at end of file +end diff --git a/assets/stylesheets/issue_age.css b/assets/stylesheets/issue_age.css index cae9bee..c59f893 100755 --- a/assets/stylesheets/issue_age.css +++ b/assets/stylesheets/issue_age.css @@ -1,7 +1,5 @@ -.fill1 { fill: #666 !important; } -.line1 { stroke: #666 !important; } -.dataPoint1, .key1 { fill: #666 !important; } +.line1 { stroke: #666666 !important; } .fill1 { fill: #666666 !important; } .key1 { fill: #666666 !important; } +.line2 { stroke: #507AAA !important; } .fill2 { fill: #BACCE0 !important; } .key2 { fill: #507AAA !important; } -.fill2 { fill: #bacce0 !important; fill-opacity: 0.8 !important; } -.line2 { stroke: #507AAA !important; } -.dataPoint2, .key2 { fill: #507AAA !important; } +.fill1, .fill2 { fill-opacity: 0.6 !important; } +.line1, .line2 { stroke-width: 2px !important; } \ No newline at end of file diff --git a/assets/stylesheets/issue_growth.css b/assets/stylesheets/issue_growth.css index 2029028..e96a0d8 100755 --- a/assets/stylesheets/issue_growth.css +++ b/assets/stylesheets/issue_growth.css @@ -1,6 +1,9 @@ -.line1 { stroke: #C00 !important; stroke-width: 4px !important; stroke-opacity: 0.8 !important; } .key1 { fill: #C00 !important; } -.line2 { stroke: #CC0 !important; stroke-width: 4px !important; stroke-opacity: 0.8 !important; } .key2 { fill: #CC0 !important; } -.line3 { stroke: #0C0 !important; stroke-width: 4px !important; stroke-opacity: 0.8 !important; } .key3 { fill: #0C0 !important; } -.line4 { stroke: #0CC !important; stroke-width: 4px !important; stroke-opacity: 0.8 !important; } .key4 { fill: #0CC !important; } -.line5 { stroke: #00C !important; stroke-width: 4px !important; stroke-opacity: 0.8 !important; } .key5 { fill: #00C !important; } -.line6 { stroke: #C0C !important; stroke-width: 4px !important; stroke-opacity: 0.8 !important; } .key6 { fill: #C0C !important; } \ No newline at end of file +.line1 { stroke: #C00 !important; } .fill1 { fill: #C00 !important; } .key1 { fill: #C00 !important; } +.line2 { stroke: #CC0 !important; } .fill2 { fill: #CC0 !important; } .key2 { fill: #CC0 !important; } +.line3 { stroke: #0C0 !important; } .fill3 { fill: #0C0 !important; } .key3 { fill: #0C0 !important; } +.line4 { stroke: #0CC !important; } .fill4 { fill: #0CC !important; } .key4 { fill: #0CC !important; } +.line5 { stroke: #00C !important; } .fill5 { fill: #00C !important; } .key5 { fill: #00C !important; } +.line6 { stroke: #C0C !important; } .fill6 { fill: #C0C !important; } .key6 { fill: #C0C !important; } + +.fill1, .fill2, .fill3, .fill4, .fill5, .fill6 { fill-opacity: 0.6 !important; } +.line1, .line2, .line3, .line4, .line5, .line6 { stroke-width: 2px !important; } \ No newline at end of file diff --git a/assets/stylesheets/target_version.css b/assets/stylesheets/target_version.css index e1421aa..9bc240c 100755 --- a/assets/stylesheets/target_version.css +++ b/assets/stylesheets/target_version.css @@ -1,12 +1,11 @@ #target_version_graph { clear: both; } -.fill1 { fill: #666 !important; } -.line1 { stroke: #666 !important; } -.key1, .dataPoint1 { fill: #666 !important; } +.line1 { stroke: #666666 !important; } .fill1 { fill: #666666 !important; } .key1 { fill: #666666 !important; } +.line2 { stroke: #009900 !important; } .fill2 { fill: #BAE0BA !important; } .key2 { fill: #009900 !important; } -.fill2 { fill: #BAE0BA !important; fill-opacity: 0.8 !important; } -.line2 { stroke: #090 !important; } -.dataPoint2, .key2 { fill: #090 !important; } +.fill1, .fill2 { fill-opacity: 0.6 !important; } +.line1, .line2 { stroke-width: 2px !important; } -.key3 { fill: #95964e !important; } -.dataPoint3 { stroke: #95964e !important; stroke-width: 10px !important; } \ No newline at end of file +.dataPoint1, .dataPoint2 { display: none !important; } +.dataPoint3 { stroke: #AA5050 !important; stroke-width: 10px !important; } +.key3 { fill: #AA5050 !important; }