Optimizing the issue growth graph. With thousands of issues, the old version ran quite slowly as it was grabbing information for each issue. This new implementation only selects what it needs to show the graph. Future versions might simplify it further by selecting issues created per week instead of per day, but I'll leave that for later.
This commit is contained in:
parent
bd5a1567f8
commit
f81d0d5160
|
@ -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
|
||||
end
|
||||
|
|
|
@ -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; }
|
|
@ -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; }
|
||||
.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; }
|
|
@ -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; }
|
||||
.dataPoint1, .dataPoint2 { display: none !important; }
|
||||
.dataPoint3 { stroke: #AA5050 !important; stroke-width: 10px !important; }
|
||||
.key3 { fill: #AA5050 !important; }
|
||||
|
|
Loading…
Reference in New Issue
Block a user