Mercurial > hg > soundsoftware-site
diff lib/redmine/helpers/gantt.rb @ 1464:261b3d9a4903 redmine-2.4
Update to Redmine 2.4 branch rev 12663
author | Chris Cannam |
---|---|
date | Tue, 14 Jan 2014 14:37:42 +0000 |
parents | 433d4f72a19b |
children | e248c7af89ec |
line wrap: on
line diff
--- a/lib/redmine/helpers/gantt.rb Fri Jun 14 09:05:06 2013 +0100 +++ b/lib/redmine/helpers/gantt.rb Tue Jan 14 14:37:42 2014 +0000 @@ -1,5 +1,5 @@ # Redmine - project management software -# Copyright (C) 2006-2012 Jean-Philippe Lang +# Copyright (C) 2006-2013 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,6 +23,12 @@ include Redmine::I18n include Redmine::Utils::DateCalculation + # Relation types that are rendered + DRAW_TYPES = { + IssueRelation::TYPE_BLOCKS => { :landscape_margin => 16, :color => '#F34F4F' }, + IssueRelation::TYPE_PRECEDES => { :landscape_margin => 20, :color => '#628FEA' } + }.freeze + # :nodoc: # Some utility methods for the PDF export class PDF @@ -136,17 +142,32 @@ ) end + # Returns a hash of the relations between the issues that are present on the gantt + # and that should be displayed, grouped by issue ids. + def relations + return @relations if @relations + if issues.any? + issue_ids = issues.map(&:id) + @relations = IssueRelation. + where(:issue_from_id => issue_ids, :issue_to_id => issue_ids, :relation_type => DRAW_TYPES.keys). + group_by(&:issue_from_id) + else + @relations = {} + end + end + # Return all the project nodes that will be displayed def projects return @projects if @projects ids = issues.collect(&:project).uniq.collect(&:id) if ids.any? # All issues projects and their visible ancestors - @projects = Project.visible.all( - :joins => "LEFT JOIN #{Project.table_name} child ON #{Project.table_name}.lft <= child.lft AND #{Project.table_name}.rgt >= child.rgt", - :conditions => ["child.id IN (?)", ids], - :order => "#{Project.table_name}.lft ASC" - ).uniq + @projects = Project.visible. + joins("LEFT JOIN #{Project.table_name} child ON #{Project.table_name}.lft <= child.lft AND #{Project.table_name}.rgt >= child.rgt"). + where("child.id IN (?)", ids). + order("#{Project.table_name}.lft ASC"). + uniq. + all else @projects = [] end @@ -194,12 +215,13 @@ @number_of_rows += 1 return if abort? issues = project_issues(project).select {|i| i.fixed_version.nil?} - sort_issues!(issues) + self.class.sort_issues!(issues) if issues render_issues(issues, options) return if abort? end versions = project_versions(project) + self.class.sort_versions!(versions) versions.each do |version| render_version(project, version, options) end @@ -228,7 +250,7 @@ return if abort? issues = version_issues(project, version) if issues - sort_issues!(issues) + self.class.sort_issues!(issues) # Indent issues options[:indent] += options[:indent_increment] render_issues(issues, options) @@ -277,7 +299,6 @@ pdf_task(options, coords, :label => label, :markers => true, :height => 0.8) end else - ActiveRecord::Base.logger.debug "Gantt#line_for_project was not given a project with a start_date" '' end end @@ -289,10 +310,18 @@ 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 @@ -303,24 +332,24 @@ def line_for_version(version, options) # Skip versions that don't have a start_date - if version.is_a?(Version) && version.start_date && version.due_date + if version.is_a?(Version) && version.due_date && version.start_date options[:zoom] ||= 1 options[:g_width] ||= (self.date_to - self.date_from + 1) * options[:zoom] coords = coordinates(version.start_date, - version.due_date, version.completed_pourcent, + version.due_date, version.completed_percent, options[:zoom]) - label = "#{h version} #{h version.completed_pourcent.to_i.to_s}%" + label = "#{h version} #{h version.completed_percent.to_i.to_s}%" 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 pdf_task(options, coords, :label => label, :markers => true, :height => 0.8) end else - ActiveRecord::Base.logger.debug "Gantt#line_for_version was not given a version with a start_date" '' end end @@ -336,6 +365,13 @@ 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 @@ -347,7 +383,7 @@ 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 @@ -378,7 +414,6 @@ pdf_task(options, coords, :label => label) end else - ActiveRecord::Base.logger.debug "GanttHelper#line_for_issue was not given an issue with a due_before" '' end end @@ -611,7 +646,7 @@ 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 @@ -638,18 +673,27 @@ coords 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) } + def calc_progress_date(start_date, end_date, progress) + start_date + (end_date - start_date + 1) * (progress / 100.0) end - # TODO: top level issues should be sorted by start date - def gantt_issue_compare(x, y) - if x.root_id == y.root_id - x.lft <=> y.lft - else - x.root_id <=> y.root_id - end + def self.sort_issues!(issues) + issues.sort! {|a, b| sort_issue_logic(a) <=> sort_issue_logic(b)} + end + + def self.sort_issue_logic(issue) + julian_date = Date.new() + ancesters_start_date = [] + current_issue = issue + begin + ancesters_start_date.unshift([current_issue.start_date || julian_date, current_issue.id]) + current_issue = current_issue.parent + end while (current_issue) + ancesters_start_date + end + + def self.sort_versions!(versions) + versions.sort! end def current_limit @@ -678,9 +722,10 @@ 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 @@ -705,6 +750,16 @@ params[:image].text(params[:indent], params[:top] + 2, subject) end + def issue_relations(issue) + rels = {} + if relations[issue.id] + relations[issue.id].each do |relation| + (rels[relation.relation_type] ||= []) << relation.issue_to_id + end + end + rels + end + def html_task(params, coords, options={}) output = '' # Renders the task bar, with progress and late @@ -714,9 +769,18 @@ style << "top:#{params[:top]}px;" style << "left:#{coords[:bar_start]}px;" style << "width:#{width}px;" - output << view.content_tag(:div, ' '.html_safe, - :style => style, - :class => "#{options[:css]} task_todo") + 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} + if options[:issue] + rels = issue_relations(options[:issue]) + if rels.present? + content_opt[:data] = {"rels" => rels.to_json} + end + end + output << view.content_tag(:div, ' '.html_safe, content_opt) if coords[:bar_late_end] width = coords[:bar_late_end] - coords[:bar_start] - 2 style = "" @@ -733,9 +797,12 @@ 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