##// END OF EJS Templates
remove trailing white-spaces from app/models/version.rb....
Toshi MARUYAMA -
r6761:e9e47673fa63
parent child
Show More
@@ -1,234 +1,234
1 1 # Redmine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 #
8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 #
13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Version < ActiveRecord::Base
19 19 after_update :update_issues_from_sharing_change
20 20 belongs_to :project
21 21 has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify
22 22 acts_as_customizable
23 23 acts_as_attachable :view_permission => :view_files,
24 24 :delete_permission => :manage_files
25 25
26 26 VERSION_STATUSES = %w(open locked closed)
27 27 VERSION_SHARINGS = %w(none descendants hierarchy tree system)
28
28
29 29 validates_presence_of :name
30 30 validates_uniqueness_of :name, :scope => [:project_id]
31 31 validates_length_of :name, :maximum => 60
32 32 validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
33 33 validates_inclusion_of :status, :in => VERSION_STATUSES
34 34 validates_inclusion_of :sharing, :in => VERSION_SHARINGS
35 35
36 36 named_scope :named, lambda {|arg| { :conditions => ["LOWER(#{table_name}.name) = LOWER(?)", arg.to_s.strip]}}
37 37 named_scope :open, :conditions => {:status => 'open'}
38 38 named_scope :visible, lambda {|*args| { :include => :project,
39 39 :conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
40 40
41 41 # Returns true if +user+ or current user is allowed to view the version
42 42 def visible?(user=User.current)
43 43 user.allowed_to?(:view_issues, self.project)
44 44 end
45
45
46 46 def start_date
47 47 @start_date ||= fixed_issues.minimum('start_date')
48 48 end
49
49
50 50 def due_date
51 51 effective_date
52 52 end
53
53
54 54 # Returns the total estimated time for this version
55 55 # (sum of leaves estimated_hours)
56 56 def estimated_hours
57 57 @estimated_hours ||= fixed_issues.leaves.sum(:estimated_hours).to_f
58 58 end
59
59
60 60 # Returns the total reported time for this version
61 61 def spent_hours
62 62 @spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f
63 63 end
64
64
65 65 def closed?
66 66 status == 'closed'
67 67 end
68 68
69 69 def open?
70 70 status == 'open'
71 71 end
72
72
73 73 # Returns true if the version is completed: due date reached and no open issues
74 74 def completed?
75 75 effective_date && (effective_date <= Date.today) && (open_issues_count == 0)
76 76 end
77 77
78 78 def behind_schedule?
79 79 if completed_pourcent == 100
80 80 return false
81 81 elsif due_date && start_date
82 82 done_date = start_date + ((due_date - start_date+1)* completed_pourcent/100).floor
83 83 return done_date <= Date.today
84 84 else
85 85 false # No issues so it's not late
86 86 end
87 87 end
88
88
89 89 # Returns the completion percentage of this version based on the amount of open/closed issues
90 90 # and the time spent on the open issues.
91 91 def completed_pourcent
92 92 if issues_count == 0
93 93 0
94 94 elsif open_issues_count == 0
95 95 100
96 96 else
97 97 issues_progress(false) + issues_progress(true)
98 98 end
99 99 end
100
100
101 101 # Returns the percentage of issues that have been marked as 'closed'.
102 102 def closed_pourcent
103 103 if issues_count == 0
104 104 0
105 105 else
106 106 issues_progress(false)
107 107 end
108 108 end
109
109
110 110 # Returns true if the version is overdue: due date reached and some open issues
111 111 def overdue?
112 112 effective_date && (effective_date < Date.today) && (open_issues_count > 0)
113 113 end
114
114
115 115 # Returns assigned issues count
116 116 def issues_count
117 117 @issue_count ||= fixed_issues.count
118 118 end
119
119
120 120 # Returns the total amount of open issues for this version.
121 121 def open_issues_count
122 122 @open_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, false], :include => :status)
123 123 end
124 124
125 125 # Returns the total amount of closed issues for this version.
126 126 def closed_issues_count
127 127 @closed_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, true], :include => :status)
128 128 end
129
129
130 130 def wiki_page
131 131 if project.wiki && !wiki_page_title.blank?
132 132 @wiki_page ||= project.wiki.find_page(wiki_page_title)
133 133 end
134 134 @wiki_page
135 135 end
136
136
137 137 def to_s; name end
138 138
139 139 def to_s_with_project
140 140 "#{project} - #{name}"
141 141 end
142
142
143 143 # Versions are sorted by effective_date and "Project Name - Version name"
144 144 # Those with no effective_date are at the end, sorted by "Project Name - Version name"
145 145 def <=>(version)
146 146 if self.effective_date
147 147 if version.effective_date
148 148 if self.effective_date == version.effective_date
149 149 "#{self.project.name} - #{self.name}" <=> "#{version.project.name} - #{version.name}"
150 150 else
151 151 self.effective_date <=> version.effective_date
152 152 end
153 153 else
154 154 -1
155 155 end
156 156 else
157 157 if version.effective_date
158 158 1
159 159 else
160 160 "#{self.project.name} - #{self.name}" <=> "#{version.project.name} - #{version.name}"
161 161 end
162 162 end
163 163 end
164
164
165 165 # Returns the sharings that +user+ can set the version to
166 166 def allowed_sharings(user = User.current)
167 167 VERSION_SHARINGS.select do |s|
168 168 if sharing == s
169 169 true
170 170 else
171 171 case s
172 172 when 'system'
173 173 # Only admin users can set a systemwide sharing
174 174 user.admin?
175 175 when 'hierarchy', 'tree'
176 176 # Only users allowed to manage versions of the root project can
177 177 # set sharing to hierarchy or tree
178 178 project.nil? || user.allowed_to?(:manage_versions, project.root)
179 179 else
180 180 true
181 181 end
182 182 end
183 183 end
184 184 end
185
185
186 186 private
187 187
188 188 # Update the issue's fixed versions. Used if a version's sharing changes.
189 189 def update_issues_from_sharing_change
190 190 if sharing_changed?
191 191 if VERSION_SHARINGS.index(sharing_was).nil? ||
192 192 VERSION_SHARINGS.index(sharing).nil? ||
193 193 VERSION_SHARINGS.index(sharing_was) > VERSION_SHARINGS.index(sharing)
194 194 Issue.update_versions_from_sharing_change self
195 195 end
196 196 end
197 197 end
198
198
199 199 # Returns the average estimated time of assigned issues
200 200 # or 1 if no issue has an estimated time
201 201 # Used to weigth unestimated issues in progress calculation
202 202 def estimated_average
203 203 if @estimated_average.nil?
204 204 average = fixed_issues.average(:estimated_hours).to_f
205 205 if average == 0
206 206 average = 1
207 207 end
208 208 @estimated_average = average
209 209 end
210 210 @estimated_average
211 211 end
212
212
213 213 # Returns the total progress of open or closed issues. The returned percentage takes into account
214 214 # the amount of estimated time set for this version.
215 215 #
216 216 # Examples:
217 217 # issues_progress(true) => returns the progress percentage for open issues.
218 218 # issues_progress(false) => returns the progress percentage for closed issues.
219 219 def issues_progress(open)
220 220 @issues_progress ||= {}
221 221 @issues_progress[open] ||= begin
222 222 progress = 0
223 223 if issues_count > 0
224 224 ratio = open ? 'done_ratio' : 100
225
225
226 226 done = fixed_issues.sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}",
227 227 :include => :status,
228 228 :conditions => ["is_closed = ?", !open]).to_f
229 229 progress = done / (estimated_average * issues_count)
230 230 end
231 231 progress
232 232 end
233 233 end
234 234 end
General Comments 0
You need to be logged in to leave comments. Login now