From 29218c7486974c8d8115b25dd5d080b1abc1430d Mon Sep 17 00:00:00 2001 From: Gregor Schmidt Date: Mon, 20 Nov 2017 13:50:59 +0100 Subject: [PATCH 1/4] Moves issue calculations into the fixed_issues relation This way, we can reuse them on refined relations, e.g. @version.fixed_issues.closed_count@ vs. @version.fixed_issues.visible.closed_count@ --- app/models/version.rb | 166 +++++++++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 69 deletions(-) diff --git a/app/models/version.rb b/app/models/version.rb index 4c559dcad..12a7be903 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -23,7 +23,97 @@ class Version < ActiveRecord::Base before_destroy :nullify_projects_default_version belongs_to :project - has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify + has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id', :dependent => :nullify do + # Returns the total estimated time for this version + # (sum of leaves estimated_hours) + def estimated_hours + @estimated_hours ||= sum(:estimated_hours).to_f + end + # + # Returns the total amount of open issues for this version. + def open_count + load_counts + @open_count + end + + # Returns the total amount of closed issues for this version. + def closed_count + load_counts + @closed_count + end + + # Returns the completion percentage of this version based on the amount of open/closed issues + # and the time spent on the open issues. + def completed_percent + if count == 0 + 0 + elsif open_count == 0 + 100 + else + issues_progress(false) + issues_progress(true) + end + end + + # Returns the percentage of issues that have been marked as 'closed'. + def closed_percent + if count == 0 + 0 + else + issues_progress(false) + end + end + + private + + def load_counts + unless @open_count + @open_count = 0 + @closed_count = 0 + self.group(:status).count.each do |status, count| + if status.is_closed? + @closed_count += count + else + @open_count += count + end + end + end + end + + # Returns the average estimated time of assigned issues + # or 1 if no issue has an estimated time + # Used to weight unestimated issues in progress calculation + def estimated_average + if @estimated_average.nil? + average = average(:estimated_hours).to_f + if average == 0 + average = 1 + end + @estimated_average = average + end + @estimated_average + end + + # Returns the total progress of open or closed issues. The returned percentage takes into account + # the amount of estimated time set for this version. + # + # Examples: + # issues_progress(true) => returns the progress percentage for open issues. + # issues_progress(false) => returns the progress percentage for closed issues. + def issues_progress(open) + @issues_progress ||= {} + @issues_progress[open] ||= begin + progress = 0 + if count > 0 + ratio = open ? 'done_ratio' : 100 + + done = open(open).sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}").to_f + progress = done / (estimated_average * count) + end + progress + end + end + end + acts_as_customizable acts_as_attachable :view_permission => :view_files, :edit_permission => :manage_files, @@ -104,7 +194,7 @@ class Version < ActiveRecord::Base # Returns the total estimated time for this version # (sum of leaves estimated_hours) def estimated_hours - @estimated_hours ||= fixed_issues.sum(:estimated_hours).to_f + fixed_issues.estimated_hours end # Returns the total reported time for this version @@ -139,22 +229,12 @@ class Version < ActiveRecord::Base # Returns the completion percentage of this version based on the amount of open/closed issues # and the time spent on the open issues. def completed_percent - if issues_count == 0 - 0 - elsif open_issues_count == 0 - 100 - else - issues_progress(false) + issues_progress(true) - end + fixed_issues.completed_percent end # Returns the percentage of issues that have been marked as 'closed'. def closed_percent - if issues_count == 0 - 0 - else - issues_progress(false) - end + fixed_issues.closed_percent end # Returns true if the version is overdue: due date reached and some open issues @@ -164,20 +244,17 @@ class Version < ActiveRecord::Base # Returns assigned issues count def issues_count - load_issue_counts - @issue_count + fixed_issues.count end # Returns the total amount of open issues for this version. def open_issues_count - load_issue_counts - @open_issues_count + fixed_issues.open_count end # Returns the total amount of closed issues for this version. def closed_issues_count - load_issue_counts - @closed_issues_count + fixed_issues.closed_count end def wiki_page @@ -284,21 +361,6 @@ class Version < ActiveRecord::Base private - def load_issue_counts - unless @issue_count - @open_issues_count = 0 - @closed_issues_count = 0 - fixed_issues.group(:status).count.each do |status, count| - if status.is_closed? - @closed_issues_count += count - else - @open_issues_count += count - end - end - @issue_count = @open_issues_count + @closed_issues_count - end - end - # Update the issue's fixed versions. Used if a version's sharing changes. def update_issues_from_sharing_change if saved_change_to_sharing? @@ -316,40 +378,6 @@ class Version < ActiveRecord::Base end end - # Returns the average estimated time of assigned issues - # or 1 if no issue has an estimated time - # Used to weight unestimated issues in progress calculation - def estimated_average - if @estimated_average.nil? - average = fixed_issues.average(:estimated_hours).to_f - if average == 0 - average = 1 - end - @estimated_average = average - end - @estimated_average - end - - # Returns the total progress of open or closed issues. The returned percentage takes into account - # the amount of estimated time set for this version. - # - # Examples: - # issues_progress(true) => returns the progress percentage for open issues. - # issues_progress(false) => returns the progress percentage for closed issues. - def issues_progress(open) - @issues_progress ||= {} - @issues_progress[open] ||= begin - progress = 0 - if issues_count > 0 - ratio = open ? 'done_ratio' : 100 - - done = fixed_issues.open(open).sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}").to_f - progress = done / (estimated_average * issues_count) - end - progress - end - end - def referenced_by_a_custom_field? CustomValue.joins(:custom_field). where(:value => id.to_s, :custom_fields => {:field_format => 'version'}).any? -- 2.14.1