Defect #23511

Updated by Toshi MARUYAMA over 5 years ago

Problem of calculating the percentage of completion of a Issue(see the attached file).

When "child" has "sub-child": estimated_hours is nil, therefore is not accounted in "average" (line 1534).

Issue.total_estimated_hours has to be accounted in this estimate.

app/model/issue.rb:

<pre><code class="ruby">

1511 def recalculate_attributes_for(issue_id)
1512 if issue_id && p = Issue.find_by_id(issue_id)
1513 if p.priority_derived?
1514 # priority = highest priority of children
1515 if priority_position = p.children.joins(:priority).maximum("#{IssuePriority.table_name}.position")
1516 p.priority = IssuePriority.find_by_position(priority_position)
1517 end
1518 end
1519
1520 if p.dates_derived?
1521 # start/due dates = lowest/highest dates of children
1522 p.start_date = p.children.minimum(:start_date)
1523 p.due_date = p.children.maximum(:due_date)
1524 if p.start_date && p.due_date && p.due_date < p.start_date
1525 p.start_date, p.due_date = p.due_date, p.start_date
1526 end
1527 end
1528
1529 if p.done_ratio_derived?
1530 # done ratio = weighted average ratio of leaves
1531 unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
1532 child_count = p.children.count
1533 if child_count > 0
1534 average = p.children.where("estimated_hours > 0").average(:estimated_hours).to_f
1535 if average == 0
1536 average = 1
1537 end
1538 done = p.children.joins(:status).
1539 sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " +
1540 "* (CASE WHEN is_closed = #{self.class.connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
1541 progress = done / (average * child_count)
1542 p.done_ratio = progress.round
1543 end
1544 end
1545 end
1546
1547 # ancestors will be recursively updated
1548 p.save(:validate => false)
1549 end
1550 end
</code></pre>

Possible Fix:

<pre><code class="diff">

--- issue.rb.ori 2016-08-01 11:44:53.220332666 -0400
+++ issue.rb 2016-08-01 11:45:07.395332666 -0400
@@ -1531,13 +1531,12 @@
unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
child_count = p.children.count
if child_count > 0
- average = p.children.where("estimated_hours > 0").average(:estimated_hours).to_f
+ average = p.total_estimated_hours / child_count
if average == 0
average = 1
end
- done = p.children.joins(:status).
- sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " +
- "* (CASE WHEN is_closed = #{self.class.connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
+ done = 0
+ p.children.each { |c| done += (c.total_estimated_hours * c.done_ratio) }
progress = done / (average * child_count)
p.done_ratio = progress.round
end
</code></pre>

**************

When one manually enter the estimated time.
In the original program, based on the average of other tasks to calculate progress. I keep the same approach in the patch :

<pre><code class="diff">

--- issue.rb.ori 2016-08-01 11:44:53.220332666 -0400
+++ issue.rb 2016-08-01 13:42:18.138330460 -0400
@@ -1531,13 +1531,19 @@
unless Issue.use_status_for_done_ratio? && p.status && p.status.default_done_ratio
child_count = p.children.count
if child_count > 0
- average = p.children.where("estimated_hours > 0").average(:estimated_hours).to_f
+ average = (p.total_estimated_hours||=0) / child_count
if average == 0
average = 1
end
- done = p.children.joins(:status).
- sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " +
- "* (CASE WHEN is_closed = #{self.class.connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f
+ done = 0
+ p.children.each do |c|
+ dr = c.closed_on.nil? ? (c.done_ratio||=0) : 100
+ unless c.total_estimated_hours.nil? || c.total_estimated_hours == 0
+ done += (c.total_estimated_hours * c.done_ratio)
+ else
+ done += average * c.done_ratio
+ end
+ end
progress = done / (average * child_count)
p.done_ratio = progress.round
end
</code></pre>