summaryrefslogtreecommitdiff
path: root/app/models/concerns/milestoneish.rb
blob: b1a7d7ec819f883b382fcda2bfdd51553d47bd5d (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
136
137
138
139
140
141
142
143
144
145
# frozen_string_literal: true

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

  def closed_issues_count(user)
    closed_state_id = Issue.available_states[:closed]

    count_issues_by_state(user)[closed_state_id].to_i
  end

  def complete?(user)
    total_issues_count(user) > 0 && total_issues_count(user) == closed_issues_count(user)
  end

  def percent_complete(user)
    closed_issues_count(user) * 100 / total_issues_count(user)
  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 issue_participants_visible_by_user(user)
    User.joins(:issue_assignees)
      .where('issue_assignees.issue_id' => issues_visible_to_user(user).select(:id))
      .distinct
  end

  def issue_labels_visible_by_user(user)
    Label.joins(:label_links)
      .where('label_links.target_id' => issues_visible_to_user(user).select(:id), 'label_links.target_type' => 'Issue')
      .distinct
  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 merge_requests_visible_to_user(user)
    memoize_per_user(user, :merge_requests_visible_to_user) do
      MergeRequestsFinder.new(user, issues_finder_params)
        .execute.where(milestone_id: milestoneish_id)
    end
  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 global_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_id).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