diff --git a/app/helpers/versions_helper.rb b/app/helpers/versions_helper.rb
index ca93abd53..bf15d425e 100644
--- a/app/helpers/versions_helper.rb
+++ b/app/helpers/versions_helper.rb
@@ -73,4 +73,27 @@ module VersionsHelper
def status_by_options_for_select(value)
options_for_select(STATUS_BY_CRITERIAS.collect {|criteria| [l("field_#{criteria}".to_sym), criteria]}, value)
end
+
+ def version_burnup_chart_data(version)
+ return nil if version.visible_fixed_issues.empty?
+ chart_start_date = (version.start_date || version.created_on).to_date
+ chart_end_date = [version.due_date, version.visible_fixed_issues.maximum(:due_date), version.visible_fixed_issues.maximum(:updated_on)].compact.max.to_date
+ line_end_date = [User.current.today, chart_end_date].min
+ step_size = ((chart_start_date..chart_end_date).count.to_f / 90).ceil
+ return nil if step_size < 1
+ reported = line_end_date.step(chart_start_date, -step_size).collect{|d| {:t => d.to_s, :y => version.visible_fixed_issues.where("#{Issue.table_name}.created_on<=?", d.end_of_day).count}}
+ closed = line_end_date.step(chart_start_date, -step_size).collect{|d| {:t => d.to_s, :y => version.visible_fixed_issues.open(false).where("#{Issue.table_name}.closed_on<=?", d.end_of_day).count}}
+ chart_data = {
+ :labels => chart_end_date.step(chart_start_date, -step_size).collect{|d|d.to_s},
+ :datasets => [
+ {:label => l(:label_reported_issues), :data => reported,
+ :borderColor => "rgba(128, 128, 128, 1)", :backgroundColor => "rgba(128, 128, 128, 0.1)", :pointBackgroundColor => "rgba(128, 128, 128, 1)",
+ :lineTension => 0, :borderWidth => 2},
+ {:label => l(:label_closed_issues), :data => closed,
+ :borderColor => "rgba(186, 224, 186, 1)", :backgroundColor => "rgba(186, 224, 186, 0.1)", :pointBackgroundColor => "rgba(186, 224, 186, 1)",
+ :lineTension => 0, :borderWidth => 2}
+ ]
+ }
+ return chart_data
+ end
end
diff --git a/app/views/versions/show.html.erb b/app/views/versions/show.html.erb
index e1b27ee0d..c2a1e00ed 100644
--- a/app/views/versions/show.html.erb
+++ b/app/views/versions/show.html.erb
@@ -51,6 +51,9 @@
<% end %>
<% end %>
+
+
+
<%= context_menu %>
<% end %>
@@ -58,3 +61,45 @@
<%= call_hook :view_versions_show_bottom, :version => @version %>
<% html_title @version.name %>
+<% if chart_data = version_burnup_chart_data(@version) %>
+ <%= javascript_tag do %>
+ function renderChart(canvas_id, title, chartData){
+ new Chart($(canvas_id), {
+ type: 'line',
+ data: chartData,
+ options: {
+ responsive: true,
+ legend: {
+ position: 'right',
+ labels: { boxWidth: 20, fontSize: 10, padding: 10 }
+ },
+ title: { display: true, text: title },
+ tooltips: {
+ callbacks: {
+ title: function(tooltipItem, data) { return '' }
+ }
+ },
+ scales: {
+ xAxes: [{
+ type: "time",
+ time: { unit: "day", displayFormats: { day: 'YYYY-MM-DD' } },
+ gridLines: { borderDash: [6, 4] },
+ ticks: { source: 'labels', autoSkip: true }
+ }],
+ yAxes: [{
+ gridLines: { borderDash: [6, 4] },
+ ticks: { min: 0, max: <%= @version.fixed_issues.count + 1 %>, stepSize: 1 }
+ }]
+ }
+ }
+ });
+ }
+ $(document).ready(function(){
+ var chartData = <%= chart_data.to_json.html_safe %>;
+ renderChart("#version_chart", "Issues burnup", chartData);
+ });
+ <% end %>
+ <% content_for :header_tags do %>
+ <%= javascript_include_tag "Chart.bundle.min" %>
+ <% end %>
+<% end %>
diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css
index a4c77d118..66ad444d3 100644
--- a/public/stylesheets/application.css
+++ b/public/stylesheets/application.css
@@ -585,6 +585,7 @@ div#roadmap .wiki h1 { font-size: 120%; }
div#roadmap .wiki h2 { font-size: 110%; }
div#roadmap h2, div#roadmap h3 { display: inline; padding-right: 0;}
body.controller-versions.action-show div#roadmap .related-issues {width:70%;}
+body.controller-versions.action-show div#roadmap .version-report-graph { width: 70%; margin: 2em 0 }
div#version-summary { float:right; width:28%; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
div#version-summary fieldset { margin-bottom: 1em; }
diff --git a/public/stylesheets/responsive.css b/public/stylesheets/responsive.css
index aa5502ee6..76a39a6e8 100644
--- a/public/stylesheets/responsive.css
+++ b/public/stylesheets/responsive.css
@@ -679,6 +679,7 @@
.version-overview table.progress {width:75%;}
div#version-summary {float:none; width:100%; margin-left:0;}
body.controller-versions.action-show div#roadmap .related-issues {width:100%;}
+ body.controller-versions.action-show div#roadmap .version-report-graph {width:100%;}
/* History and Changeset */
div#issue-changesets {
diff --git a/test/helpers/version_helper_test.rb b/test/helpers/version_helper_test.rb
index e1c83e225..e43846c5c 100644
--- a/test/helpers/version_helper_test.rb
+++ b/test/helpers/version_helper_test.rb
@@ -53,4 +53,41 @@ class VersionsHelperTest < Redmine::HelperTest
version.project = Project.find(5)
assert_match /^\/issues\?/, version_filtered_issues_path(version)
end
+
+ def test_version_burnup_chart_data_should_return_chart_data
+ User.any_instance.stubs(:today).returns(3.days.after.to_date)
+ version = Version.create!(:project => Project.find(1), :name => 'test', :due_date => 5.days.after)
+ issue = Issue.create!(:project => version.project, :fixed_version => version,
+ :tracker => version.project.trackers.first, :subject => 'text', :author => User.current,
+ :start_date => 1.days.after)
+ chart_data = {
+ :labels => [5.days.after.to_date.to_s, 4.days.after.to_date.to_s, 3.days.after.to_date.to_s, 2.days.after.to_date.to_s, 1.days.after.to_date.to_s],
+ :datasets => [
+ {:label => l(:label_reported_issues),
+ :data => [{:t => 3.days.after.to_date.to_s, :y => 1}, {:t => 2.days.after.to_date.to_s, :y => 1}, {:t => 1.days.after.to_date.to_s, :y => 1}],
+ :borderColor => "rgba(128, 128, 128, 1)", :backgroundColor => "rgba(128, 128, 128, 0.1)", :pointBackgroundColor => "rgba(128, 128, 128, 1)",
+ :lineTension => 0, :borderWidth => 2},
+ {:label => l(:label_closed_issues),
+ :data => [{:t => 3.days.after.to_date.to_s, :y => 0}, {:t => 2.days.after.to_date.to_s, :y => 0}, {:t => 1.days.after.to_date.to_s, :y => 0}],
+ :borderColor => "rgba(186, 224, 186, 1)", :backgroundColor => "rgba(186, 224, 186, 0.1)", :pointBackgroundColor => "rgba(186, 224, 186, 1)",
+ :lineTension => 0, :borderWidth => 2}
+ ]
+ }
+ assert_equal chart_data, version_burnup_chart_data(version)
+ end
+
+ def test_version_burnup_chart_data_should_return_nil_when_visible_fixed_issues_empty
+ version = Version.create!(:project => Project.find(1), :name => 'test')
+ version.visible_fixed_issues.destroy_all
+ assert_empty version.visible_fixed_issues
+ assert_nil version_burnup_chart_data(version)
+ end
+
+ def test_version_burnup_chart_data_should_return_nil_when_order_of_start_date_and_due_date_is_reversed
+ version = Version.create!(:project => Project.find(1), :name => 'test', :due_date => 10.days.after)
+ issue = Issue.create!(:project => version.project, :fixed_version => version,
+ :tracker => version.project.trackers.first, :subject => 'text', :author => User.current,
+ :start_date => 11.days.after)
+ assert_nil version_burnup_chart_data(version)
+ end
end