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 |
|
class Version < ActiveRecord::Base
|
19 |
|
include Redmine::SafeAttributes
|
20 |
|
|
21 |
|
after_update :update_issues_from_sharing_change
|
22 |
|
after_save :update_default_project_version
|
23 |
|
before_destroy :nullify_projects_default_version
|
|
18 |
module FixedIssuesExtension
|
|
19 |
# Returns the total estimated time for this version
|
|
20 |
# (sum of leaves estimated_hours)
|
|
21 |
def estimated_hours
|
|
22 |
@estimated_hours ||= sum(:estimated_hours).to_f
|
|
23 |
end
|
|
24 |
#
|
|
25 |
# Returns the total amount of open issues for this version.
|
|
26 |
def open_count
|
|
27 |
load_counts
|
|
28 |
@open_count
|
|
29 |
end
|
24 |
30 |
|
25 |
|
belongs_to :project
|
26 |
|
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify do
|
27 |
|
# Returns the total estimated time for this version
|
28 |
|
# (sum of leaves estimated_hours)
|
29 |
|
def estimated_hours
|
30 |
|
@estimated_hours ||= sum(:estimated_hours).to_f
|
31 |
|
end
|
32 |
|
#
|
33 |
|
# Returns the total amount of open issues for this version.
|
34 |
|
def open_count
|
35 |
|
load_counts
|
36 |
|
@open_count
|
37 |
|
end
|
|
31 |
# Returns the total amount of closed issues for this version.
|
|
32 |
def closed_count
|
|
33 |
load_counts
|
|
34 |
@closed_count
|
|
35 |
end
|
38 |
36 |
|
39 |
|
# Returns the total amount of closed issues for this version.
|
40 |
|
def closed_count
|
41 |
|
load_counts
|
42 |
|
@closed_count
|
|
37 |
# Returns the completion percentage of this version based on the amount of open/closed issues
|
|
38 |
# and the time spent on the open issues.
|
|
39 |
def completed_percent
|
|
40 |
if count == 0
|
|
41 |
0
|
|
42 |
elsif open_count == 0
|
|
43 |
100
|
|
44 |
else
|
|
45 |
issues_progress(false) + issues_progress(true)
|
43 |
46 |
end
|
|
47 |
end
|
44 |
48 |
|
45 |
|
# Returns the completion percentage of this version based on the amount of open/closed issues
|
46 |
|
# and the time spent on the open issues.
|
47 |
|
def completed_percent
|
48 |
|
if count == 0
|
49 |
|
0
|
50 |
|
elsif open_count == 0
|
51 |
|
100
|
52 |
|
else
|
53 |
|
issues_progress(false) + issues_progress(true)
|
54 |
|
end
|
|
49 |
# Returns the percentage of issues that have been marked as 'closed'.
|
|
50 |
def closed_percent
|
|
51 |
if count == 0
|
|
52 |
0
|
|
53 |
else
|
|
54 |
issues_progress(false)
|
55 |
55 |
end
|
|
56 |
end
|
56 |
57 |
|
57 |
|
# Returns the percentage of issues that have been marked as 'closed'.
|
58 |
|
def closed_percent
|
59 |
|
if count == 0
|
60 |
|
0
|
61 |
|
else
|
62 |
|
issues_progress(false)
|
63 |
|
end
|
64 |
|
end
|
|
58 |
private
|
65 |
59 |
|
66 |
|
private
|
67 |
|
|
68 |
|
def load_counts
|
69 |
|
unless @open_count
|
70 |
|
@open_count = 0
|
71 |
|
@closed_count = 0
|
72 |
|
self.group(:status).count.each do |status, count|
|
73 |
|
if status.is_closed?
|
74 |
|
@closed_count += count
|
75 |
|
else
|
76 |
|
@open_count += count
|
77 |
|
end
|
|
60 |
def load_counts
|
|
61 |
unless @open_count
|
|
62 |
@open_count = 0
|
|
63 |
@closed_count = 0
|
|
64 |
self.group(:status).count.each do |status, count|
|
|
65 |
if status.is_closed?
|
|
66 |
@closed_count += count
|
|
67 |
else
|
|
68 |
@open_count += count
|
78 |
69 |
end
|
79 |
70 |
end
|
80 |
71 |
end
|
|
72 |
end
|
81 |
73 |
|
82 |
|
# Returns the average estimated time of assigned issues
|
83 |
|
# or 1 if no issue has an estimated time
|
84 |
|
# Used to weight unestimated issues in progress calculation
|
85 |
|
def estimated_average
|
86 |
|
if @estimated_average.nil?
|
87 |
|
average = average(:estimated_hours).to_f
|
88 |
|
if average == 0
|
89 |
|
average = 1
|
90 |
|
end
|
91 |
|
@estimated_average = average
|
|
74 |
# Returns the average estimated time of assigned issues
|
|
75 |
# or 1 if no issue has an estimated time
|
|
76 |
# Used to weight unestimated issues in progress calculation
|
|
77 |
def estimated_average
|
|
78 |
if @estimated_average.nil?
|
|
79 |
average = average(:estimated_hours).to_f
|
|
80 |
if average == 0
|
|
81 |
average = 1
|
92 |
82 |
end
|
93 |
|
@estimated_average
|
|
83 |
@estimated_average = average
|
94 |
84 |
end
|
95 |
|
|
96 |
|
# Returns the total progress of open or closed issues. The returned percentage takes into account
|
97 |
|
# the amount of estimated time set for this version.
|
98 |
|
#
|
99 |
|
# Examples:
|
100 |
|
# issues_progress(true) => returns the progress percentage for open issues.
|
101 |
|
# issues_progress(false) => returns the progress percentage for closed issues.
|
102 |
|
def issues_progress(open)
|
103 |
|
@issues_progress ||= {}
|
104 |
|
@issues_progress[open] ||= begin
|
105 |
|
progress = 0
|
106 |
|
if count > 0
|
107 |
|
ratio = open ? 'done_ratio' : 100
|
108 |
|
|
109 |
|
done = open(open).sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}").to_f
|
110 |
|
progress = done / (estimated_average * count)
|
111 |
|
end
|
112 |
|
progress
|
|
85 |
@estimated_average
|
|
86 |
end
|
|
87 |
|
|
88 |
# Returns the total progress of open or closed issues. The returned percentage takes into account
|
|
89 |
# the amount of estimated time set for this version.
|
|
90 |
#
|
|
91 |
# Examples:
|
|
92 |
# issues_progress(true) => returns the progress percentage for open issues.
|
|
93 |
# issues_progress(false) => returns the progress percentage for closed issues.
|
|
94 |
def issues_progress(open)
|
|
95 |
@issues_progress ||= {}
|
|
96 |
@issues_progress[open] ||= begin
|
|
97 |
progress = 0
|
|
98 |
if count > 0
|
|
99 |
ratio = open ? 'done_ratio' : 100
|
|
100 |
|
|
101 |
done = open(open).sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}").to_f
|
|
102 |
progress = done / (estimated_average * count)
|
113 |
103 |
end
|
|
104 |
progress
|
114 |
105 |
end
|
115 |
106 |
end
|
|
107 |
end
|
|
108 |
|
|
109 |
class Version < ActiveRecord::Base
|
|
110 |
include Redmine::SafeAttributes
|
|
111 |
|
|
112 |
after_update :update_issues_from_sharing_change
|
|
113 |
after_save :update_default_project_version
|
|
114 |
before_destroy :nullify_projects_default_version
|
|
115 |
|
|
116 |
belongs_to :project
|
|
117 |
has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify, :extend => FixedIssuesExtension
|
116 |
118 |
|
117 |
119 |
acts_as_customizable
|
118 |
120 |
acts_as_attachable :view_permission => :view_files,
|