Feature #34830 » issues-burndown-in-version-detail.patch
| app/helpers/versions_helper.rb | ||
|---|---|---|
| 104 | 104 |
end |
| 105 | 105 |
end |
| 106 | 106 |
end |
| 107 | ||
| 108 |
def issues_burndown_chart_data(version) |
|
| 109 |
return nil if version.visible_fixed_issues.empty? |
|
| 110 |
chart_start_date = (version.start_date || version.created_on).to_date |
|
| 111 |
chart_end_date = [version.due_date, version.visible_fixed_issues.maximum(:due_date), version.visible_fixed_issues.maximum(:updated_on)].compact.max.to_date |
|
| 112 |
line_end_date = [User.current.today, chart_end_date].min |
|
| 113 |
step_size = ((chart_start_date..chart_end_date).count.to_f / 90).ceil |
|
| 114 |
issues = version.visible_fixed_issues |
|
| 115 |
return nil if step_size < 1 |
|
| 116 |
total_closed = chart_start_date.step(line_end_date, step_size).collect{|d| {:t => d.to_s, :y => issues.where("#{Issue.table_name}.closed_on IS NULL OR #{Issue.table_name}.closed_on>=?", d.end_of_day).count}}
|
|
| 117 |
open = chart_start_date.step(line_end_date, step_size).collect{|d| {:t => d.to_s, :y => issues.where("#{Issue.table_name}.created_on<=?", d.end_of_day).count - issues.open(false).where("#{Issue.table_name}.closed_on<=?", d.end_of_day).count}}
|
|
| 118 |
chart_data = {
|
|
| 119 |
:labels => chart_start_date.step(chart_end_date, step_size).collect{|d|d.to_s},
|
|
| 120 |
:datasets => [ |
|
| 121 |
{:label => l(:label_ideal), :data => [{:t => chart_start_date.to_s, :y => total_closed.first[:y]}, {:t => chart_end_date.to_s, :y => 0}],
|
|
| 122 |
:backgroundColor => "rgba(0, 0, 0, 0)", |
|
| 123 |
:lineTension => 0, :borderWidth => 2, :borderDash => [5, 2], :spanGaps => true}, |
|
| 124 |
{:label => l(:label_total_substract_closed), :data => total_closed,
|
|
| 125 |
:borderColor => "rgba(186, 224, 186, 1)", :backgroundColor => "rgba(0, 0, 0, 0)", :pointBackgroundColor => "rgba(186, 224, 186, 1)", |
|
| 126 |
:lineTension => 0, :borderWidth => 2, :borderDash => [5, 2]}, |
|
| 127 |
{:label => l(:label_open), :data => open,
|
|
| 128 |
:borderColor => "rgba(186, 224, 186, 1)", :backgroundColor => "rgba(186, 224, 186, 0.1)", :pointBackgroundColor => "rgba(186, 224, 186, 1)", |
|
| 129 |
:lineTension => 0, :borderWidth => 2} |
|
| 130 |
] |
|
| 131 |
} |
|
| 132 |
return chart_data |
|
| 133 |
end |
|
| 107 | 134 |
end |
| app/views/versions/show.html.erb | ||
|---|---|---|
| 52 | 52 |
<% end %> |
| 53 | 53 |
</table> |
| 54 | 54 |
<% end %> |
| 55 |
<div class="version-report-graph"> |
|
| 56 |
<canvas id="version_chart"></canvas> |
|
| 57 |
</div> |
|
| 55 | 58 |
<%= context_menu %> |
| 56 | 59 |
<% end %> |
| 57 | 60 |
</div> |
| ... | ... | |
| 59 | 62 |
<%= call_hook :view_versions_show_bottom, :version => @version %> |
| 60 | 63 | |
| 61 | 64 |
<% html_title @version.name %> |
| 65 |
<% if chart_data = issues_burndown_chart_data(@version) %> |
|
| 66 |
<%= javascript_tag do %> |
|
| 67 |
function renderChart(canvas_id, title, y_label, chartData){
|
|
| 68 |
new Chart($(canvas_id), {
|
|
| 69 |
type: 'line', |
|
| 70 |
data: chartData, |
|
| 71 |
options: {
|
|
| 72 |
responsive: true, |
|
| 73 |
legend: {
|
|
| 74 |
position: 'right', |
|
| 75 |
labels: { boxWidth: 20, fontSize: 10, padding: 10 }
|
|
| 76 |
}, |
|
| 77 |
title: { display: true, text: title },
|
|
| 78 |
tooltips: {
|
|
| 79 |
callbacks: {
|
|
| 80 |
title: function(tooltipItem, data) { return '' }
|
|
| 81 |
} |
|
| 82 |
}, |
|
| 83 |
scales: {
|
|
| 84 |
xAxes: [{
|
|
| 85 |
type: "time", |
|
| 86 |
time: { unit: "day", displayFormats: { day: 'YYYY-MM-DD' } },
|
|
| 87 |
gridLines: { borderDash: [6, 4] },
|
|
| 88 |
ticks: { source: 'labels', autoSkip: true }
|
|
| 89 |
}], |
|
| 90 |
yAxes: [{
|
|
| 91 |
scaleLabel: { display: true, labelString: y_label },
|
|
| 92 |
gridLines: { borderDash: [6, 4] },
|
|
| 93 |
ticks: { min: 0, max: <%= @version.fixed_issues.count %>, stepSize: 1 }
|
|
| 94 |
}] |
|
| 95 |
} |
|
| 96 |
} |
|
| 97 |
}); |
|
| 98 |
} |
|
| 99 |
$(document).ready(function(){
|
|
| 100 |
var title = "<%= l(:label_issues_burndown) %>"; |
|
| 101 |
var y_label = "<%= l(:label_issues_burndown_y_label) %>"; |
|
| 102 |
var chartData = <%= chart_data.to_json.html_safe %>; |
|
| 103 |
renderChart("#version_chart", title, y_label, chartData);
|
|
| 104 |
}); |
|
| 105 |
<% end %> |
|
| 106 |
<% content_for :header_tags do %> |
|
| 107 |
<%= javascript_include_tag "Chart.bundle.min" %> |
|
| 108 |
<% end %> |
|
| 109 |
<% end %> |
|
| config/locales/en.yml | ||
|---|---|---|
| 731 | 731 |
label_total: Total |
| 732 | 732 |
label_total_plural: Totals |
| 733 | 733 |
label_total_time: Total time |
| 734 |
label_issues_burndown: burndown |
|
| 735 |
label_issues_burndown_y_label: Number of Issues |
|
| 736 |
label_ideal: Ideal |
|
| 737 |
label_total_substract_closed: Total - Closed |
|
| 738 |
label_open: Open |
|
| 734 | 739 |
label_permissions: Permissions |
| 735 | 740 |
label_current_status: Current status |
| 736 | 741 |
label_new_statuses_allowed: New statuses allowed |
| public/stylesheets/application.css | ||
|---|---|---|
| 630 | 630 |
div#roadmap .wiki h2 { font-size: 110%; }
|
| 631 | 631 |
div#roadmap h2, div#roadmap h3 {padding-right: 0;}
|
| 632 | 632 |
body.controller-versions.action-show div#roadmap .related-issues {width:70%;}
|
| 633 |
body.controller-versions.action-show div#roadmap .version-report-graph { width: 70%; margin: 2em 0 }
|
|
| 633 | 634 | |
| 634 | 635 |
div#version-summary { float:right; width:28%; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
|
| 635 | 636 |
div#version-summary fieldset { margin-bottom: 1em; }
|
| public/stylesheets/responsive.css | ||
|---|---|---|
| 666 | 666 |
.version-overview table.progress {width:75%;}
|
| 667 | 667 |
div#version-summary {float:none; width:100%; margin-left:0;}
|
| 668 | 668 |
body.controller-versions.action-show div#roadmap .related-issues {width:100%;}
|
| 669 |
body.controller-versions.action-show div#roadmap .version-report-graph {width:100%;}
|
|
| 669 | 670 | |
| 670 | 671 |
/* History and Changeset */ |
| 671 | 672 |
div#issue-changesets {
|
| test/helpers/version_helper_test.rb | ||
|---|---|---|
| 25 | 25 |
fixtures :projects, :versions, :enabled_modules, |
| 26 | 26 |
:users, :members, :roles, :member_roles, |
| 27 | 27 |
:trackers, :projects_trackers, |
| 28 |
:enumerations, |
|
| 28 | 29 |
:issue_statuses |
| 29 | 30 | |
| 30 | 31 |
def test_version_filtered_issues_path_sharing_none |
| ... | ... | |
| 120 | 121 |
# href should contain param tracker_id=2 because for tracker_id 1, user has only readonly permissions on fixed_version_id |
| 121 | 122 |
assert_select_in link_to_new_issue(version, project), '[href=?]', '/projects/ecookbook/issues/new?back_url=%2Fversions%2F3&issue%5Bfixed_version_id%5D=3&issue%5Btracker_id%5D=2' |
| 122 | 123 |
end |
| 124 | ||
| 125 |
def test_issues_burndown_chart_data_should_return_chart_data |
|
| 126 |
User.any_instance.stubs(:today).returns(3.days.after.to_date) |
|
| 127 |
version = Version.create!(:project => Project.find(1), :name => 'test', :due_date => 5.days.after.to_date) |
|
| 128 |
issue = Issue.create!(:project => version.project, :fixed_version => version, |
|
| 129 |
:priority => IssuePriority.find_by_name('Normal'),
|
|
| 130 |
:tracker => version.project.trackers.first, :subject => 'text', :author => User.current, |
|
| 131 |
:start_date => 1.days.after.to_date) |
|
| 132 |
chart_data = {
|
|
| 133 |
:labels => (1.days.after.to_date..5.days.after.to_date).map { |d| d.to_s },
|
|
| 134 |
:datasets => [ |
|
| 135 |
{:label => l(:label_ideal), :data => [{:t => 1.days.after.to_date.to_s, :y => 1}, {:t => 5.days.after.to_date.to_s, :y => 0}],
|
|
| 136 |
:backgroundColor => "rgba(0, 0, 0, 0)", |
|
| 137 |
:lineTension => 0, :borderWidth => 2, :borderDash => [5, 2], :spanGaps => true}, |
|
| 138 |
{:label => l(:label_total_substract_closed), :data => [{:t => 1.days.after.to_date.to_s, :y => 1}, {:t => 2.days.after.to_date.to_s, :y => 1}, {:t => 3.days.after.to_date.to_s, :y => 1}],
|
|
| 139 |
:borderColor => "rgba(186, 224, 186, 1)", :backgroundColor => "rgba(0, 0, 0, 0)", :pointBackgroundColor => "rgba(186, 224, 186, 1)", |
|
| 140 |
:lineTension => 0, :borderWidth => 2, :borderDash => [5, 2]}, |
|
| 141 |
{:label => l(:label_open), :data => [{:t => 1.days.after.to_date.to_s, :y => 1}, {:t => 2.days.after.to_date.to_s, :y => 1}, {:t => 3.days.after.to_date.to_s, :y => 1}],
|
|
| 142 |
:borderColor => "rgba(186, 224, 186, 1)", :backgroundColor => "rgba(186, 224, 186, 0.1)", :pointBackgroundColor => "rgba(186, 224, 186, 1)", |
|
| 143 |
:lineTension => 0, :borderWidth => 2} |
|
| 144 |
] |
|
| 145 |
} |
|
| 146 |
assert_equal chart_data, issues_burndown_chart_data(version) |
|
| 147 |
end |
|
| 148 | ||
| 149 |
def test_issues_burndown_chart_data_should_return_nil_when_visible_fixed_issues_empty |
|
| 150 |
version = Version.create!(:project => Project.find(1), :name => 'test') |
|
| 151 |
version.visible_fixed_issues.destroy_all |
|
| 152 |
assert_empty version.visible_fixed_issues |
|
| 153 |
assert_nil issues_burndown_chart_data(version) |
|
| 154 |
end |
|
| 155 | ||
| 156 |
def test_issues_burndown_chart_data_should_return_nil_when_order_of_start_date_and_due_date_is_reversed |
|
| 157 |
version = Version.create!(:project => Project.find(1), :name => 'test', :due_date => 10.days.after) |
|
| 158 |
issue = Issue.create!(:project => version.project, :fixed_version => version, |
|
| 159 |
:priority => IssuePriority.find_by_name('Normal'),
|
|
| 160 |
:tracker => version.project.trackers.first, :subject => 'text', :author => User.current, |
|
| 161 |
:start_date => 11.days.after) |
|
| 162 |
assert_nil issues_burndown_chart_data(version) |
|
| 163 |
end |
|
| 123 | 164 |
end |
- « Previous
- 1
- 2
- Next »