summaryrefslogtreecommitdiff
path: root/app/models/concerns/milestoneish.rb
blob: 39372c4f68b5891e0fb208dcb5864f5047b86ca5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# frozen_string_literal: true

module Milestoneish
  def closed_items_count(user)
    memoize_per_user(user, :closed_items_count) do
      (count_issues_by_state(user)['closed'] || 0) + merge_requests.closed_and_merged.size
    end
  end

  def total_items_count(user)
    memoize_per_user(user, :total_items_count) do
      total_issues_count(user) + merge_requests.size
    end
  end

  def total_issues_count(user)
    count_issues_by_state(user).values.sum
  end

  def complete?(user)
    total_items_count(user) > 0 && total_items_count(user) == closed_items_count(user)
  end

  def percent_complete(user)
    ((closed_items_count(user) * 100) / total_items_count(user)).abs
  rescue ZeroDivisionError
    0
  end

  def remaining_days
    return 0 if !due_date || expired?

    (due_date - Date.today).to_i
  end

  def elapsed_days
    return 0 if !start_date || start_date.future?

    (Date.today - start_date).to_i
  end

  def issues_visible_to_user(user)
    memoize_per_user(user, :issues_visible_to_user) do
      IssuesFinder.new(user, issues_finder_params)
        .execute.preload(:assignees).where(milestone_id: milestoneish_id)
    end
  end

  def merge_requests_visible_to_user(user)
    memoize_per_user(user, :merge_requests_visible_to_user) do
      MergeRequestsFinder.new(user, {})
        .execute.where(milestone_id: milestoneish_id)
    end
  end

  def sorted_issues(user)
    issues_visible_to_user(user).preload_associations.sort_by_attribute('label_priority')
  end

  def sorted_merge_requests(user)
    merge_requests_visible_to_user(user).sort_by_attribute('label_priority')
  end

  def upcoming?
    start_date && start_date.future?
  end

  def expires_at
    if due_date
      if due_date.past?
        "expired on #{due_date.to_s(:medium)}"
      else
        "expires on #{due_date.to_s(:medium)}"
      end
    end
  end

  def expired?
    due_date && due_date.past?
  end

  def group_milestone?
    false
  end

  def project_milestone?
    false
  end

  def legacy_group_milestone?
    false
  end

  def dashboard_milestone?
    false
  end

  def total_issue_time_spent
    @total_issue_time_spent ||= issues.joins(:timelogs).sum(:time_spent)
  end

  def human_total_issue_time_spent
    Gitlab::TimeTrackingFormatter.output(total_issue_time_spent)
  end

  def total_issue_time_estimate
    @total_issue_time_estimate ||= issues.sum(:time_estimate)
  end

  def human_total_issue_time_estimate
    Gitlab::TimeTrackingFormatter.output(total_issue_time_estimate)
  end

  def count_issues_by_state(user)
    memoize_per_user(user, :count_issues_by_state) do
      issues_visible_to_user(user).reorder(nil).group(:state).count
    end
  end

  private

  def memoize_per_user(user, method_name)
    memoized_users[method_name][user&.id] ||= yield
  end

  def memoized_users
    @memoized_users ||= Hash.new { |h, k| h[k] = {} }
  end

  # override in a class that includes this module to get a faster query
  # from IssuesFinder
  def issues_finder_params
    {}
  end
end