From 237f297f03e2f3fac6a41b3c0dadec6d85bcfb12 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Sun, 20 Jan 2013 12:30:40 +0000 Subject: [PATCH] Gantt progress lines (#12122). Patch by Toshi MARUYAMA. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@11210 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/views/gantts/show.html.erb | 39 +++++++++++++++++++- config/locales/en.yml | 1 + config/locales/ja.yml | 1 + lib/redmine/helpers/gantt.rb | 39 ++++++++++++++++---- public/javascripts/gantt.js | 65 +++++++++++++++++++++++++++++++++- 5 files changed, 136 insertions(+), 9 deletions(-) diff --git a/app/views/gantts/show.html.erb b/app/views/gantts/show.html.erb index caec4c3c2..463e0f1b0 100644 --- a/app/views/gantts/show.html.erb +++ b/app/views/gantts/show.html.erb @@ -12,6 +12,39 @@ <%= render :partial => 'queries/filters', :locals => {:query => @query} %> +
+ <%= l(:label_display) %> +
+ + + + + +
+
+ <%= l(:label_related_issues) %> + +
+
+
+ <%= l(:label_gantt_progress_line) %> + +
+
+
+

<%= gantt_zoom_link(@gantt, :in) %> @@ -229,7 +262,7 @@ style += "width:10px;" style += "border-left: 1px dashed red;" %> - <%= content_tag(:div, ' '.html_safe, :style => style) %> + <%= content_tag(:div, ' '.html_safe, :style => style, :id => 'today_line') %> <% end %> <% style = "" @@ -279,4 +312,8 @@ var issue_relation_type = <%= raw Redmine::Helpers::Gantt::DRAW_TYPES.to_json %>; $(document).ready(drawGanttHandler); $(window).resize(drawGanttHandler); + $(function() { + $("#draw_rels").change(drawGanttHandler); + $("#draw_progress_line").change(drawGanttHandler); + }); <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index d9bf367fa..5aec6c15f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -886,6 +886,7 @@ en: label_cross_project_tree: With project tree label_cross_project_hierarchy: With project hierarchy label_cross_project_system: With all projects + label_gantt_progress_line: Progress line button_login: Login button_submit: Submit diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 71668f28b..944c54d97 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -833,6 +833,7 @@ ja: label_git_report_last_commit: ファイルとディレクトリの最新コミットを表示する label_parent_revision: 親 label_child_revision: 子 + label_gantt_progress_line: イナズマ線 button_login: ログイン button_submit: 送信 diff --git a/lib/redmine/helpers/gantt.rb b/lib/redmine/helpers/gantt.rb index d1eeb9589..32be436bd 100644 --- a/lib/redmine/helpers/gantt.rb +++ b/lib/redmine/helpers/gantt.rb @@ -308,10 +308,18 @@ module Redmine html_class << 'icon icon-package ' html_class << (version.behind_schedule? ? 'version-behind-schedule' : '') << " " html_class << (version.overdue? ? 'version-overdue' : '') + html_class << ' version-closed' unless version.open? + if version.start_date && version.due_date && version.completed_pourcent + progress_date = calc_progress_date(version.start_date, + version.due_date, version.completed_pourcent) + html_class << ' behind-start-date' if progress_date < self.date_from + html_class << ' over-end-date' if progress_date > self.date_to + end s = view.link_to_version(version).html_safe subject = view.content_tag(:span, s, :class => html_class).html_safe - html_subject(options, subject, :css => "version-name") + html_subject(options, subject, :css => "version-name", + :id => "version-#{version.id}") when :image image_subject(options, version.to_s_with_project) when :pdf @@ -332,7 +340,8 @@ module Redmine label = h("#{version.project} -") + label unless @project && @project == version.project case options[:format] when :html - html_task(options, coords, :css => "version task", :label => label, :markers => true) + html_task(options, coords, :css => "version task", + :label => label, :markers => true, :version => version) when :image image_task(options, coords, :label => label, :markers => true, :height => 3) when :pdf @@ -354,6 +363,13 @@ module Redmine css_classes << ' issue-overdue' if issue.overdue? css_classes << ' issue-behind-schedule' if issue.behind_schedule? css_classes << ' icon icon-issue' unless Setting.gravatar_enabled? && issue.assigned_to + css_classes << ' issue-closed' if issue.closed? + if issue.start_date && issue.due_before && issue.done_ratio + progress_date = calc_progress_date(issue.start_date, + issue.due_before, issue.done_ratio) + css_classes << ' behind-start-date' if progress_date < self.date_from + css_classes << ' over-end-date' if progress_date > self.date_to + end s = "".html_safe if issue.assigned_to.present? assigned_string = l(:field_assigned_to) + ": " + issue.assigned_to.name @@ -365,7 +381,7 @@ module Redmine s << view.link_to_issue(issue).html_safe subject = view.content_tag(:span, s, :class => css_classes).html_safe html_subject(options, subject, :css => "issue-subject", - :title => issue.subject) + "\n" + :title => issue.subject, :id => "issue-#{issue.id}") + "\n" when :image image_subject(options, issue.subject) when :pdf @@ -628,7 +644,7 @@ module Redmine coords[:bar_end] = self.date_to - self.date_from + 1 end if progress - progress_date = start_date + (end_date - start_date + 1) * (progress / 100.0) + progress_date = calc_progress_date(start_date, end_date, progress) if progress_date > self.date_from && progress_date > start_date if progress_date < self.date_to coords[:bar_progress_end] = progress_date - self.date_from @@ -655,6 +671,10 @@ module Redmine coords end + def calc_progress_date(start_date, end_date, progress) + start_date + (end_date - start_date + 1) * (progress / 100.0) + end + # Sorts a collection of issues by start_date, due_date, id for gantt rendering def sort_issues!(issues) issues.sort! { |a, b| gantt_issue_compare(a, b) } @@ -695,9 +715,10 @@ module Redmine def html_subject(params, subject, options={}) style = "position: absolute;top:#{params[:top]}px;left:#{params[:indent]}px;" style << "width:#{params[:subject_width] - params[:indent]}px;" if params[:subject_width] - output = view.content_tag('div', subject, + output = view.content_tag(:div, subject, :class => options[:css], :style => style, - :title => options[:title]) + :title => options[:title], + :id => options[:id]) @subjects << output output end @@ -742,6 +763,7 @@ module Redmine style << "left:#{coords[:bar_start]}px;" style << "width:#{width}px;" html_id = "task-todo-issue-#{options[:issue].id}" if options[:issue] + html_id = "task-todo-version-#{options[:version].id}" if options[:version] content_opt = {:style => style, :class => "#{options[:css]} task_todo", :id => html_id} @@ -768,9 +790,12 @@ module Redmine style << "top:#{params[:top]}px;" style << "left:#{coords[:bar_start]}px;" style << "width:#{width}px;" + html_id = "task-done-issue-#{options[:issue].id}" if options[:issue] + html_id = "task-done-version-#{options[:version].id}" if options[:version] output << view.content_tag(:div, ' '.html_safe, :style => style, - :class => "#{options[:css]} task_done") + :class => "#{options[:css]} task_done", + :id => html_id) end end # Renders the markers diff --git a/public/javascripts/gantt.js b/public/javascripts/gantt.js index 066a15884..295487c09 100644 --- a/public/javascripts/gantt.js +++ b/public/javascripts/gantt.js @@ -98,6 +98,66 @@ function drawRelations() { }); } +function getProgressLinesArray() { + var arr = new Array(); + var today_left = $('#today_line').position().left; + arr.push({left: today_left, top: 0}); + $.each($('div.issue-subject, div.version-name'), function(index, element) { + var t = $(element).position().top - draw_top ; + var h = ($(element).height() / 9); + var element_top_upper = t - h; + var element_top_center = t + (h * 3); + var element_top_lower = t + (h * 8); + var issue_closed = $(element).children('span').hasClass('issue-closed'); + var version_closed = $(element).children('span').hasClass('version-closed'); + if (issue_closed || version_closed) { + arr.push({left: today_left, top: element_top_center}); + } else { + var issue_done = $("#task-done-" + $(element).attr("id")); + var is_behind_start = $(element).children('span').hasClass('behind-start-date'); + var is_over_end = $(element).children('span').hasClass('over-end-date'); + if (is_over_end) { + arr.push({left: draw_right, top: element_top_upper, is_right_edge: true}); + arr.push({left: draw_right, top: element_top_lower, is_right_edge: true, none_stroke: true}); + } else if (issue_done.size() > 0) { + var done_left = issue_done.first().position().left + + issue_done.first().width(); + arr.push({left: done_left, top: element_top_center}); + } else if (is_behind_start) { + arr.push({left: 0 , top: element_top_upper, is_left_edge: true}); + arr.push({left: 0 , top: element_top_lower, is_left_edge: true, none_stroke: true}); + } else { + var todo_left = today_left; + var issue_todo = $("#task-todo-" + $(element).attr("id")); + if (issue_todo.size() > 0){ + todo_left = issue_todo.first().position().left; + } + arr.push({left: Math.min(today_left, todo_left), top: element_top_center}); + } + } + }); + return arr; +} + +function drawGanttProgressLines() { + var arr = getProgressLinesArray(); + var color = $("#today_line") + .css("border-left-color"); + var i; + for(i = 1 ; i < arr.length ; i++) { + if (!("none_stroke" in arr[i]) && + (!("is_right_edge" in arr[i - 1] && "is_right_edge" in arr[i]) && + !("is_left_edge" in arr[i - 1] && "is_left_edge" in arr[i])) + ) { + var x1 = (arr[i - 1].left == 0) ? 0 : arr[i - 1].left + draw_left; + var x2 = (arr[i].left == 0) ? 0 : arr[i].left + draw_left; + draw_gantt.path(["M", x1, arr[i - 1].top, + "L", x2, arr[i].top]) + .attr({stroke: color, "stroke-width": 2}); + } + } +} + function drawGanttHandler() { var folder = document.getElementById('gantt_draw_area'); if(draw_gantt != null) @@ -105,5 +165,8 @@ function drawGanttHandler() { else draw_gantt = Raphael(folder); setDrawArea(); - drawRelations(); + if ($("#draw_progress_line").attr('checked')) + drawGanttProgressLines(); + if ($("#draw_rels").attr('checked')) + drawRelations(); }