Gantt: do not ignore project filter (#7000, #7352), do not display empty projects/versions, and display shared versions used in other projects (#5817, #6476, #6604).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@5077 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
b1cc8511c7
commit
ec8d9a7911
|
@ -1,5 +1,5 @@
|
||||||
# Redmine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2008 Jean-Philippe Lang
|
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
@ -98,51 +98,25 @@ module Redmine
|
||||||
common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months })
|
common_params.merge({:year => (date_from >> months).year, :month => (date_from >> months).month, :zoom => zoom, :months => months })
|
||||||
end
|
end
|
||||||
|
|
||||||
### Extracted from the HTML view/helpers
|
|
||||||
# Returns the number of rows that will be rendered on the Gantt chart
|
# Returns the number of rows that will be rendered on the Gantt chart
|
||||||
def number_of_rows
|
def number_of_rows
|
||||||
return @number_of_rows if @number_of_rows
|
return @number_of_rows if @number_of_rows
|
||||||
|
|
||||||
rows = if @project
|
rows = projects.inject(0) {|total, p| total += number_of_rows_on_project(p)}
|
||||||
number_of_rows_on_project(@project)
|
|
||||||
else
|
|
||||||
Project.roots.visible.has_module('issue_tracking').inject(0) do |total, project|
|
|
||||||
total += number_of_rows_on_project(project)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
rows > @max_rows ? @max_rows : rows
|
rows > @max_rows ? @max_rows : rows
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the number of rows that will be used to list a project on
|
# Returns the number of rows that will be used to list a project on
|
||||||
# the Gantt chart. This will recurse for each subproject.
|
# the Gantt chart. This will recurse for each subproject.
|
||||||
def number_of_rows_on_project(project)
|
def number_of_rows_on_project(project)
|
||||||
# Remove the project requirement for Versions because it will
|
return 0 unless projects.include?(project)
|
||||||
# restrict issues to only be on the current project. This
|
|
||||||
# ends up missing issues which are assigned to shared versions.
|
|
||||||
@query.project = nil if @query.project
|
|
||||||
|
|
||||||
# One Root project
|
|
||||||
count = 1
|
count = 1
|
||||||
# Issues without a Version
|
count += project_issues(project).size
|
||||||
count += project.issues.for_gantt.without_version.with_query(@query).count
|
count += project_versions(project).size
|
||||||
|
|
||||||
# Versions
|
|
||||||
count += project.versions.count
|
|
||||||
|
|
||||||
# Issues on the Versions
|
|
||||||
project.versions.each do |version|
|
|
||||||
count += version.fixed_issues.for_gantt.with_query(@query).count
|
|
||||||
end
|
|
||||||
|
|
||||||
# Subprojects
|
|
||||||
project.children.visible.has_module('issue_tracking').each do |subproject|
|
|
||||||
count += number_of_rows_on_project(subproject)
|
|
||||||
end
|
|
||||||
|
|
||||||
count
|
count
|
||||||
end
|
end
|
||||||
|
|
||||||
# Renders the subjects of the Gantt chart, the left side.
|
# Renders the subjects of the Gantt chart, the left side.
|
||||||
def subjects(options={})
|
def subjects(options={})
|
||||||
render(options.merge(:only => :subjects)) unless @subjects_rendered
|
render(options.merge(:only => :subjects)) unless @subjects_rendered
|
||||||
|
@ -155,20 +129,60 @@ module Redmine
|
||||||
@lines
|
@lines
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns issues that will be rendered
|
||||||
|
def issues
|
||||||
|
@issues ||= @query.issues(
|
||||||
|
:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
|
||||||
|
:order => "#{Project.table_name}.lft ASC, #{Issue.table_name}.id ASC",
|
||||||
|
:limit => @max_rows
|
||||||
|
)
|
||||||
|
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
|
||||||
|
else
|
||||||
|
@projects = []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the issues that belong to +project+
|
||||||
|
def project_issues(project)
|
||||||
|
@issues_by_project ||= issues.group_by(&:project)
|
||||||
|
@issues_by_project[project] || []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the distinct versions of the issues that belong to +project+
|
||||||
|
def project_versions(project)
|
||||||
|
project_issues(project).collect(&:fixed_version).compact.uniq
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the issues that belong to +project+ and are assigned to +version+
|
||||||
|
def version_issues(project, version)
|
||||||
|
project_issues(project).select {|issue| issue.fixed_version == version}
|
||||||
|
end
|
||||||
|
|
||||||
def render(options={})
|
def render(options={})
|
||||||
options = {:indent => 4, :render => :subject, :format => :html}.merge(options)
|
options = {:top => 0, :top_increment => 20, :indent_increment => 20, :render => :subject, :format => :html}.merge(options)
|
||||||
|
indent = options[:indent] || 4
|
||||||
|
|
||||||
@subjects = '' unless options[:only] == :lines
|
@subjects = '' unless options[:only] == :lines
|
||||||
@lines = '' unless options[:only] == :subjects
|
@lines = '' unless options[:only] == :subjects
|
||||||
@number_of_rows = 0
|
@number_of_rows = 0
|
||||||
|
|
||||||
if @project
|
Project.project_tree(projects) do |project, level|
|
||||||
render_project(@project, options)
|
options[:indent] = indent + level * options[:indent_increment]
|
||||||
else
|
render_project(project, options)
|
||||||
Project.roots.visible.has_module('issue_tracking').each do |project|
|
break if abort?
|
||||||
render_project(project, options)
|
|
||||||
break if abort?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@subjects_rendered = true unless options[:only] == :lines
|
@subjects_rendered = true unless options[:only] == :lines
|
||||||
|
@ -178,10 +192,6 @@ module Redmine
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_project(project, options={})
|
def render_project(project, options={})
|
||||||
options[:top] = 0 unless options.key? :top
|
|
||||||
options[:indent_increment] = 20 unless options.key? :indent_increment
|
|
||||||
options[:top_increment] = 20 unless options.key? :top_increment
|
|
||||||
|
|
||||||
subject_for_project(project, options) unless options[:only] == :lines
|
subject_for_project(project, options) unless options[:only] == :lines
|
||||||
line_for_project(project, options) unless options[:only] == :subjects
|
line_for_project(project, options) unless options[:only] == :subjects
|
||||||
|
|
||||||
|
@ -190,26 +200,18 @@ module Redmine
|
||||||
@number_of_rows += 1
|
@number_of_rows += 1
|
||||||
return if abort?
|
return if abort?
|
||||||
|
|
||||||
# Second, Issues without a version
|
issues = project_issues(project).select {|i| i.fixed_version.nil?}
|
||||||
issues = project.issues.for_gantt.without_version.with_query(@query).all(:limit => current_limit)
|
|
||||||
sort_issues!(issues)
|
sort_issues!(issues)
|
||||||
if issues
|
if issues
|
||||||
render_issues(issues, options)
|
render_issues(issues, options)
|
||||||
return if abort?
|
return if abort?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Third, Versions
|
versions = project_versions(project)
|
||||||
project.versions.sort.each do |version|
|
versions.each do |version|
|
||||||
render_version(version, options)
|
render_version(project, version, options)
|
||||||
return if abort?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fourth, subprojects
|
|
||||||
project.children.visible.has_module('issue_tracking').each do |project|
|
|
||||||
render_project(project, options)
|
|
||||||
return if abort?
|
|
||||||
end unless project.leaf?
|
|
||||||
|
|
||||||
# Remove indent to hit the next sibling
|
# Remove indent to hit the next sibling
|
||||||
options[:indent] -= options[:indent_increment]
|
options[:indent] -= options[:indent_increment]
|
||||||
end
|
end
|
||||||
|
@ -229,7 +231,7 @@ module Redmine
|
||||||
options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
|
options[:indent] -= (options[:indent_increment] * @issue_ancestors.size)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_version(version, options={})
|
def render_version(project, version, options={})
|
||||||
# Version header
|
# Version header
|
||||||
subject_for_version(version, options) unless options[:only] == :lines
|
subject_for_version(version, options) unless options[:only] == :lines
|
||||||
line_for_version(version, options) unless options[:only] == :subjects
|
line_for_version(version, options) unless options[:only] == :subjects
|
||||||
|
@ -238,12 +240,7 @@ module Redmine
|
||||||
@number_of_rows += 1
|
@number_of_rows += 1
|
||||||
return if abort?
|
return if abort?
|
||||||
|
|
||||||
# Remove the project requirement for Versions because it will
|
issues = version_issues(project, version)
|
||||||
# restrict issues to only be on the current project. This
|
|
||||||
# ends up missing issues which are assigned to shared versions.
|
|
||||||
@query.project = nil if @query.project
|
|
||||||
|
|
||||||
issues = version.fixed_issues.for_gantt.with_query(@query).all(:limit => current_limit)
|
|
||||||
if issues
|
if issues
|
||||||
sort_issues!(issues)
|
sort_issues!(issues)
|
||||||
# Indent issues
|
# Indent issues
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# redMine - project management software
|
# Redmine - project management software
|
||||||
# Copyright (C) 2006-2008 Jean-Philippe Lang
|
# Copyright (C) 2006-2011 Jean-Philippe Lang
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
# modify it under the terms of the GNU General Public License
|
# modify it under the terms of the GNU General Public License
|
||||||
|
@ -95,15 +95,9 @@ class Redmine::Helpers::GanttTest < ActiveSupport::TestCase
|
||||||
setup do
|
setup do
|
||||||
create_gantt
|
create_gantt
|
||||||
end
|
end
|
||||||
|
|
||||||
should "clear the @query.project so cross-project issues and versions can be counted" do
|
|
||||||
assert @gantt.query.project
|
|
||||||
@gantt.number_of_rows_on_project(@project)
|
|
||||||
assert_nil @gantt.query.project
|
|
||||||
end
|
|
||||||
|
|
||||||
should "count 1 for the project itself" do
|
should "count 0 for an empty the project" do
|
||||||
assert_equal 1, @gantt.number_of_rows_on_project(@project)
|
assert_equal 0, @gantt.number_of_rows_on_project(@project)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "count the number of issues without a version" do
|
should "count the number of issues without a version" do
|
||||||
|
@ -111,12 +105,6 @@ class Redmine::Helpers::GanttTest < ActiveSupport::TestCase
|
||||||
assert_equal 2, @gantt.number_of_rows_on_project(@project)
|
assert_equal 2, @gantt.number_of_rows_on_project(@project)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "count the number of versions" do
|
|
||||||
@project.versions << Version.generate!
|
|
||||||
@project.versions << Version.generate!
|
|
||||||
assert_equal 3, @gantt.number_of_rows_on_project(@project)
|
|
||||||
end
|
|
||||||
|
|
||||||
should "count the number of issues on versions, including cross-project" do
|
should "count the number of issues on versions, including cross-project" do
|
||||||
version = Version.generate!
|
version = Version.generate!
|
||||||
@project.versions << version
|
@project.versions << version
|
||||||
|
@ -124,21 +112,6 @@ class Redmine::Helpers::GanttTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
assert_equal 3, @gantt.number_of_rows_on_project(@project)
|
assert_equal 3, @gantt.number_of_rows_on_project(@project)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "recursive and count the number of rows on each subproject" do
|
|
||||||
@project.versions << Version.generate! # +1
|
|
||||||
|
|
||||||
@subproject = Project.generate!(:enabled_module_names => ['issue_tracking']) # +1
|
|
||||||
@subproject.set_parent!(@project)
|
|
||||||
@subproject.issues << Issue.generate_for_project!(@subproject) # +1
|
|
||||||
@subproject.issues << Issue.generate_for_project!(@subproject) # +1
|
|
||||||
|
|
||||||
@subsubproject = Project.generate!(:enabled_module_names => ['issue_tracking']) # +1
|
|
||||||
@subsubproject.set_parent!(@subproject)
|
|
||||||
@subsubproject.issues << Issue.generate_for_project!(@subsubproject) # +1
|
|
||||||
|
|
||||||
assert_equal 7, @gantt.number_of_rows_on_project(@project) # +1 for self
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: more of an integration test
|
# TODO: more of an integration test
|
||||||
|
@ -183,6 +156,18 @@ class Redmine::Helpers::GanttTest < ActiveSupport::TestCase
|
||||||
@response.body = @gantt.subjects
|
@response.body = @gantt.subjects
|
||||||
assert_select "div.version-name[style*=left:24px]"
|
assert_select "div.version-name[style*=left:24px]"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "without assigned issues" do
|
||||||
|
setup do
|
||||||
|
@version = Version.generate!(:effective_date => 2.week.from_now.to_date, :sharing => 'none', :name => 'empty_version')
|
||||||
|
@project.versions << @version
|
||||||
|
end
|
||||||
|
|
||||||
|
should "not be rendered" do
|
||||||
|
@response.body = @gantt.subjects
|
||||||
|
assert_select "div.version-name a", :text => /#{@version.name}/, :count => 0
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "issue" do
|
context "issue" do
|
||||||
|
@ -196,6 +181,31 @@ class Redmine::Helpers::GanttTest < ActiveSupport::TestCase
|
||||||
assert_select "div.issue-subject[style*=left:44px]"
|
assert_select "div.issue-subject[style*=left:44px]"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "assigned to a shared version of another project" do
|
||||||
|
setup do
|
||||||
|
p = Project.generate!
|
||||||
|
p.trackers << @tracker
|
||||||
|
p.enabled_module_names = [:issue_tracking]
|
||||||
|
@shared_version = Version.generate!(:sharing => 'system')
|
||||||
|
p.versions << @shared_version
|
||||||
|
# Reassign the issue to a shared version of another project
|
||||||
|
|
||||||
|
@issue = Issue.generate!(:fixed_version => @shared_version,
|
||||||
|
:subject => "gantt#assigned_to_shared_version",
|
||||||
|
:tracker => @tracker,
|
||||||
|
:project => @project,
|
||||||
|
:done_ratio => 30,
|
||||||
|
:start_date => Date.yesterday,
|
||||||
|
:due_date => 1.week.from_now.to_date)
|
||||||
|
@project.issues << @issue
|
||||||
|
end
|
||||||
|
|
||||||
|
should "be rendered" do
|
||||||
|
@response.body = @gantt.subjects
|
||||||
|
assert_select "div.issue-subject", /#{@issue.subject}/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "with subtasks" do
|
context "with subtasks" do
|
||||||
setup do
|
setup do
|
||||||
attrs = {:project => @project, :tracker => @tracker, :fixed_version => @version}
|
attrs = {:project => @project, :tracker => @tracker, :fixed_version => @version}
|
||||||
|
|
Loading…
Reference in New Issue