summaryrefslogtreecommitdiff
path: root/app/models/global_milestone.rb
blob: dc2f68171903b4c2374f0aea1fd22dc9cdaaaef4 (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
146
147
148
149
150
151
152
153
154
155
class GlobalMilestone
  include Milestoneish

  EPOCH = DateTime.parse('1970-01-01')
  STATE_COUNT_HASH = { opened: 0, closed: 0, all: 0 }.freeze

  attr_accessor :title, :milestones
  alias_attribute :name, :title

  def for_display
    @first_milestone
  end

  def self.build_collection(projects, params)
    params =
      { project_ids: projects.map(&:id), state: params[:state] }

    child_milestones = MilestonesFinder.new(params).execute

    milestones = child_milestones.select(:id, :title).group_by(&:title).map do |title, grouped|
      milestones_relation = Milestone.where(id: grouped.map(&:id))
      new(title, milestones_relation)
    end

    milestones.sort_by { |milestone| milestone.due_date || EPOCH }
  end

  def self.build(projects, title)
    child_milestones = Milestone.of_projects(projects).where(title: title)
    return if child_milestones.blank?

    new(title, child_milestones)
  end

  def self.states_count(projects, group = nil)
    legacy_group_milestones_count = legacy_group_milestone_states_count(projects)
    group_milestones_count = group_milestones_states_count(group)

    legacy_group_milestones_count.merge(group_milestones_count) do |k, legacy_group_milestones_count, group_milestones_count|
      legacy_group_milestones_count + group_milestones_count
    end
  end

  def self.group_milestones_states_count(group)
    return STATE_COUNT_HASH unless group

    params = { group_ids: [group.id], state: 'all' }

    relation = MilestonesFinder.new(params).execute
    grouped_by_state = relation.reorder(nil).group(:state).count

    {
      opened: grouped_by_state['active'] || 0,
      closed: grouped_by_state['closed'] || 0,
      all: grouped_by_state.values.sum
    }
  end

  # Counts the legacy group milestones which must be grouped by title
  def self.legacy_group_milestone_states_count(projects)
    return STATE_COUNT_HASH unless projects

    params = { project_ids: projects.map(&:id), state: 'all' }

    relation = MilestonesFinder.new(params).execute
    project_milestones_by_state_and_title = relation.reorder(nil).group(:state, :title).count

    opened = count_by_state(project_milestones_by_state_and_title, 'active')
    closed = count_by_state(project_milestones_by_state_and_title, 'closed')
    all = project_milestones_by_state_and_title.map { |(_, title), _| title }.uniq.count

    {
      opened: opened,
      closed: closed,
      all: all
    }
  end

  def self.count_by_state(milestones_by_state_and_title, state)
    milestones_by_state_and_title.count do |(milestone_state, _), _|
      milestone_state == state
    end
  end
  private_class_method :count_by_state

  def initialize(title, milestones)
    @title = title
    @name = title
    @milestones = milestones
    @first_milestone = milestones.find {|m| m.description.present? } || milestones.first
  end

  def milestoneish_ids
    milestones.select(:id)
  end

  def safe_title
    @title.to_slug.normalize.to_s
  end

  def projects
    @projects ||= Project.for_milestones(milestoneish_ids)
  end

  def state
    milestones.each do |milestone|
      return 'active' if milestone.state != 'closed'
    end

    'closed'
  end

  def active?
    state == 'active'
  end

  def closed?
    state == 'closed'
  end

  def issues
    @issues ||= Issue.of_milestones(milestoneish_ids).includes(:project, :assignees, :labels)
  end

  def merge_requests
    @merge_requests ||= MergeRequest.of_milestones(milestoneish_ids).includes(:target_project, :assignee, :labels)
  end

  def participants
    @participants ||= milestones.map(&:participants).flatten.uniq
  end

  def labels
    @labels ||= GlobalLabel.build_collection(milestones.includes(:labels).map(&:labels).flatten)
                           .sort_by!(&:title)
  end

  def due_date
    return @due_date if defined?(@due_date)

    @due_date =
      if @milestones.all? { |x| x.due_date == @milestones.first.due_date }
        @milestones.first.due_date
      end
  end

  def start_date
    return @start_date if defined?(@start_date)

    @start_date =
      if @milestones.all? { |x| x.start_date == @milestones.first.start_date }
        @milestones.first.start_date
      end
  end
end