Project

General

Profile

Patch #43933 ยป optimize-load_visible_total_spent_hours.patch

Go MAEDA, 2026-04-06 17:14

View differences:

app/models/issue.rb
1243 1243

  
1244 1244
  # Preloads visible total spent time for a collection of issues
1245 1245
  def self.load_visible_total_spent_hours(issues, user=User.current)
1246
    if issues.any?
1246
    return if issues.empty?
1247

  
1248
    # For leaf issues, `total_spent_hours` equals `spent_hours`, so reuse
1249
    # the preloaded visible spent hours to avoid the expensive query below.
1250
    optimizable_leaf_issues, remaining_issues = issues.partition do |issue|
1251
      issue.leaf? &&
1252
        issue.instance_variable_defined?(:@spent_hours) &&
1253
        !issue.instance_variable_get(:@spent_hours).nil?
1254
    end
1255

  
1256
    optimizable_leaf_issues.each do |issue|
1257
      issue.instance_variable_set :@total_spent_hours, issue.spent_hours
1258
    end
1259

  
1260
    if remaining_issues.any?
1247 1261
      hours_by_issue_id = TimeEntry.visible(user).joins(:issue).
1248 1262
        joins("JOIN #{Issue.table_name} parent ON parent.root_id = #{Issue.table_name}.root_id" +
1249 1263
          " AND parent.lft <= #{Issue.table_name}.lft AND parent.rgt >= #{Issue.table_name}.rgt").
1250
        where("parent.id IN (?)", issues.map(&:id)).group("parent.id").sum(:hours)
1251
      issues.each do |issue|
1264
        where("parent.id IN (?)", remaining_issues.map(&:id)).group("parent.id").sum(:hours)
1265
      remaining_issues.each do |issue|
1252 1266
        issue.instance_variable_set :@total_spent_hours, (hours_by_issue_id[issue.id] || 0.0)
1253 1267
      end
1254 1268
    end
test/unit/issue_test.rb
222 222
    assert_include 'Parent task is invalid', issue.errors.full_messages
223 223
  end
224 224

  
225
  def test_load_visible_total_spent_hours_should_use_loaded_spent_hours_for_leaf_issues
226
    issue = Issue.find(1)
227
    assert issue.leaf?
228
    # preloaded visible spent hours value is stored in @spent_hours instance variable
229
    issue.instance_variable_set(:@spent_hours, 3.5)
230
    TimeEntry.expects(:visible).never
231

  
232
    Issue.load_visible_total_spent_hours([issue])
233

  
234
    assert_equal 3.5, issue.instance_variable_get(:@total_spent_hours)
235
  end
236

  
225 237
  def assert_visibility_match(user, issues)
226 238
    assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
227 239
  end
    (1-1/1)