OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
openproject/app/models/sprint.rb

139 lines
5.8 KiB

require 'date'
class Sprint < Version
unloadable
validate :start_and_end_dates
named_scope :open_sprints, lambda { |project|
{
:order => 'sprint_start_date ASC, effective_date ASC',
:conditions => [ "versions.status = 'open' and versions.project_id = ?", project.id ]
}
}
named_scope :order_by_date, :order => 'sprint_start_date ASC, effective_date ASC'
named_scope :order_by_name, :order => "#{Version.table_name}.name ASC"
named_scope :apply_to, lambda { |project| {:include => :project,
:conditions => ["#{Version.table_name}.project_id = #{project.id}" +
" OR (#{Project.table_name}.status = #{Project::STATUS_ACTIVE} AND (" +
" #{Version.table_name}.sharing = 'system'" +
" OR (#{Project.table_name}.lft >= #{project.root.lft} AND #{Project.table_name}.rgt <= #{project.root.rgt} AND #{Version.table_name}.sharing = 'tree')" +
" OR (#{Project.table_name}.lft < #{project.lft} AND #{Project.table_name}.rgt > #{project.rgt} AND #{Version.table_name}.sharing IN ('hierarchy', 'descendants'))" +
" OR (#{Project.table_name}.lft > #{project.lft} AND #{Project.table_name}.rgt < #{project.rgt} AND #{Version.table_name}.sharing = 'hierarchy')" +
"))"]}}
named_scope :displayed_left, lambda { |project| { :joins => sanitize_sql_array(["LEFT OUTER JOIN (SELECT * from #{VersionSetting.table_name}" +
" WHERE project_id = ? ) version_settings" +
" ON version_settings.version_id = versions.id",
project.id]),
:conditions => ["(version_settings.project_id = ? AND version_settings.display = ?)" +
" OR (version_settings.project_id is NULL)",
project.id, VersionSetting::DISPLAY_LEFT] } }
named_scope :displayed_right, lambda { |project| { :include => :version_settings,
:conditions => ["version_settings.project_id = ? AND version_settings.display = ?",
project.id, VersionSetting::DISPLAY_RIGHT] } }
def stories(project, options = {} )
Story.sprint_backlog(project, self, options)
end
def points
return stories.inject(0){|sum, story| sum + story.story_points.to_i}
end
def has_wiki_page
return false if wiki_page_title.blank?
page = project.wiki.find_page(self.wiki_page_title)
return false if !page
template = project.wiki.find_page(Setting.plugin_redmine_backlogs[:wiki_template])
return false if template && page.text == template.text
return true
end
def wiki_page
if ! project.wiki
return ''
end
self.update_attribute(:wiki_page_title, Wiki.titleize(self.name)) if wiki_page_title.blank?
page = project.wiki.find_page(self.wiki_page_title)
template = project.wiki.find_page(Setting.plugin_redmine_backlogs[:wiki_template])
if template and not page
page = WikiPage.new(:wiki => project.wiki, :title => self.wiki_page_title)
page.content = WikiContent.new
page.content.text = "h1. #{self.name}\n\n#{template.text}"
page.save!
end
return wiki_page_title
end
def days(cutoff = nil, alldays = false)
# assumes mon-fri are working days, sat-sun are not. this
# assumption is not globally right, we need to make this configurable.
cutoff = self.effective_date if cutoff.nil?
return (self.sprint_start_date .. cutoff).select {|d| alldays || (d.wday > 0 and d.wday < 6) }
end
def eta
return nil if ! self.start_date
dpp = self.project.scrum_statistics.info[:average_days_per_point]
return nil if !dpp
# assume 5 out of 7 are working days
return self.start_date + Integer(self.points * dpp * 7.0/5)
end
def has_burndown?
!!(self.effective_date and self.sprint_start_date)
end
def activity
bd = self.burndown('up')
return false if !bd
# assume a sprint is active if it's only 2 days old
return true if bd.remaining_hours.size <= 2
return Issue.exists?(['fixed_version_id = ? and ((updated_on between ? and ?) or (created_on between ? and ?))', self.id, -2.days.from_now, Time.now, -2.days.from_now, Time.now])
end
def burndown(project, burn_direction = nil)
return nil if not self.has_burndown?
@cached_burndown ||= Burndown.new(self, project, burn_direction)
return @cached_burndown
end
def self.generate_burndown(only_current = true)
if only_current
conditions = ["? between sprint_start_date and effective_date", Date.today]
else
conditions = "1 = 1"
end
Version.find(:all, :conditions => conditions).each { |sprint|
sprint.burndown
}
end
def impediments(project)
Impediment.find(:all, :conditions => {:fixed_version_id => self, :project_id => project})
end
private
def start_and_end_dates
errors.add_to_base("Sprint cannot end before it starts") if self.effective_date && self.sprint_start_date && self.sprint_start_date >= self.effective_date
end
end