Rewrite the Gantt chart. #6276

This version of the Gantt chart supports nested charts. So Projects,
Versions, and Issues will be nested underneath their parents correctly.

Additional features:

* Move all Gantt code to Redmine::Helpers::Gantt class instead of having it in
  the Gantt class, controller, and view
* Recursive and nest sub-projects
* Recursive and nest versions
* Recursive and nest issues
* Draw a line showing when a Project is active and it's progress
* Draw a line showing when a Version is active and it's progress
* Show a version's % complete
* Change the color of Projects, Versions, and Issues if they are late or
  behind schedule
* Added Project#start_date and #due_date
* Added Project#completed_percent
* Use a mini-gravatar on the Gantt chart
* Added tests for the Gantt rendering

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4072 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Eric Davis
2010-09-10 03:09:02 +00:00
parent 8d52608dba
commit bdb3937e0f
29 changed files with 1879 additions and 403 deletions
+26 -1
View File
@@ -62,10 +62,28 @@ class Issue < ActiveRecord::Base
named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
named_scope :recently_updated, :order => "#{self.table_name}.updated_on DESC"
named_scope :recently_updated, :order => "#{Issue.table_name}.updated_on DESC"
named_scope :with_limit, lambda { |limit| { :limit => limit} }
named_scope :on_active_project, :include => [:status, :project, :tracker],
:conditions => ["#{Project.table_name}.status=#{Project::STATUS_ACTIVE}"]
named_scope :for_gantt, lambda {
{
:include => [:tracker, :status, :assigned_to, :priority, :project, :fixed_version],
:order => "#{Issue.table_name}.due_date ASC, #{Issue.table_name}.start_date ASC, #{Issue.table_name}.id ASC"
}
}
named_scope :without_version, lambda {
{
:conditions => { :fixed_version_id => nil}
}
}
named_scope :with_query, lambda {|query|
{
:conditions => Query.merge_conditions(query.statement)
}
}
before_create :default_assign
before_save :reschedule_following_issues, :close_duplicates, :update_done_ratio_from_issue_status
@@ -357,6 +375,13 @@ class Issue < ActiveRecord::Base
def overdue?
!due_date.nil? && (due_date < Date.today) && !status.is_closed?
end
# Is the amount of work done less than it should for the due date
def behind_schedule?
return false if start_date.nil? || due_date.nil?
done_date = start_date + ((due_date - start_date+1)* done_ratio/100).floor
return done_date <= Date.today
end
# Users the issue can be assigned to
def assignable_users
+44
View File
@@ -412,6 +412,50 @@ class Project < ActiveRecord::Base
def short_description(length = 255)
description.gsub(/^(.{#{length}}[^\n\r]*).*$/m, '\1...').strip if description
end
# The earliest start date of a project, based on it's issues and versions
def start_date
if module_enabled?(:issue_tracking)
[
issues.minimum('start_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.minimum('start_date')}
].flatten.compact.min
end
end
# The latest due date of an issue or version
def due_date
if module_enabled?(:issue_tracking)
[
issues.maximum('due_date'),
shared_versions.collect(&:effective_date),
shared_versions.collect {|v| v.fixed_issues.maximum('due_date')}
].flatten.compact.max
end
end
def overdue?
active? && !due_date.nil? && (due_date < Date.today)
end
# Returns the percent completed for this project, based on the
# progress on it's versions.
def completed_percent(options={:include_subprojects => false})
if options.delete(:include_subprojects)
total = self_and_descendants.collect(&:completed_percent).sum
total / self_and_descendants.count
else
if versions.count > 0
total = versions.collect(&:completed_pourcent).sum
total / versions.count
else
100
end
end
end
# Return true if this project is allowed to do the specified action.
# action can be:
+12
View File
@@ -73,6 +73,18 @@ class Version < ActiveRecord::Base
def completed?
effective_date && (effective_date <= Date.today) && (open_issues_count == 0)
end
def behind_schedule?
if completed_pourcent == 100
return false
elsif due_date && fixed_issues.present? && fixed_issues.minimum('start_date') # TODO: should use #start_date but that method is wrong...
start_date = fixed_issues.minimum('start_date')
done_date = start_date + ((due_date - start_date+1)* completed_pourcent/100).floor
return done_date <= Date.today
else
false # No issues so it's not late
end
end
# Returns the completion percentage of this version based on the amount of open/closed issues
# and the time spent on the open issues.