summaryrefslogtreecommitdiff
path: root/app/models/concerns/milestoneish.rb
blob: 5f24564dc56088843739ffb960dc68ce5cc74000 (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
# frozen_string_literal: true

module Milestoneish
  DISPLAY_ISSUES_LIMIT = 3000

  def total_issues_count
    @total_issues_count ||= Milestones::IssuesCountService.new(self).count
  end

  def closed_issues_count
    @close_issues_count ||= Milestones::ClosedIssuesCountService.new(self).count
  end

  def opened_issues_count
    total_issues_count - closed_issues_count
  end

  def complete?
    total_issues_count > 0 && total_issues_count == closed_issues_count
  end

  def percent_complete
    closed_issues_count * 100 / total_issues_count
  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)
    # This method is used on milestone view to filter opened assigned, opened unassigned and closed issues columns.
    # We want a limit of DISPLAY_ISSUES_LIMIT for total issues present on all columns.
    limited_ids =
      issues_visible_to_user(user).sort_by_attribute('label_priority').limit(DISPLAY_ISSUES_LIMIT)

    Issue
      .where(id: Issue.select(:id).from(limited_ids))
      .preload_associated_models
      .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 total_time_spent
    @total_time_spent ||= issues.joins(:timelogs).sum(:time_spent) + merge_requests.joins(:timelogs).sum(:time_spent)
  end

  def human_total_time_spent
    Gitlab::TimeTrackingFormatter.output(total_time_spent)
  end

  def total_time_estimate
    @total_time_estimate ||= issues.sum(:time_estimate) + merge_requests.sum(:time_estimate)
  end

  def human_total_time_estimate
    Gitlab::TimeTrackingFormatter.output(total_time_estimate)
  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