Class: Milestone
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Milestone
- Includes:
- FromUnion, Importable, Milestoneish, Sortable, Timebox
- Defined in:
- app/models/milestone.rb
Defined Under Namespace
Classes: Predefined
Constant Summary
Constants included from Milestoneish
Milestoneish::DISPLAY_ISSUES_LIMIT
Constants included from Timebox
Timebox::Any, Timebox::None, Timebox::Started, Timebox::Upcoming
Constants included from Gitlab::SQL::Pattern
Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING, Gitlab::SQL::Pattern::REGEX_QUOTED_WORD
Constants included from CacheMarkdownField
CacheMarkdownField::INVALIDATED_BY
Constants included from AtomicInternalId
AtomicInternalId::MissingValueError
Constants inherited from ApplicationRecord
Instance Attribute Summary
Attributes included from Importable
Class Method Summary collapse
- .link_reference_pattern ⇒ Object
- .min_chars_for_partial_matching ⇒ Object
- .reference_pattern ⇒ Object
- .reference_prefix ⇒ Object
-
.search_title(query) ⇒ Object
Searches for timeboxes with a matching title.
- .sort_by_attribute(method) ⇒ Object
- .sort_with_expired_last(method) ⇒ Object
- .states_count(projects, groups = nil) ⇒ Object
- .upcoming_ids(projects, groups) ⇒ Object
- .with_web_entity_associations ⇒ Object
Instance Method Summary collapse
- #author_id ⇒ Object
- #can_be_closed? ⇒ Boolean
- #for_display ⇒ Object
- #parent ⇒ Object
- #participants ⇒ Object
- #subgroup_milestone? ⇒ Boolean
Methods included from Milestoneish
#closed_issues_count, #complete?, #elapsed_days, #expired, #expired?, #expires_at, #human_total_time_estimate, #human_total_time_spent, #issue_labels_visible_by_user, #issue_participants_visible_by_user, #issues_visible_to_user, #merge_requests_visible_to_user, #opened_issues_count, #percent_complete, #remaining_days, #sorted_issues, #sorted_merge_requests, #total_issues_count, #total_merge_requests_count, #total_time_estimate, #total_time_spent, #upcoming?
Methods included from Timebox
#group_timebox?, #merge_requests_enabled?, #project_timebox?, #reference_link_text, #resource_parent, #safe_title, #timebox_name, #title=, #to_ability_name, #to_reference, #weight_available?
Methods included from StripAttribute
Methods included from Referable
#referable_inspect, #reference_link_text, #to_reference, #to_reference_base
Methods included from IidRoutes
Methods included from CacheMarkdownField
#attribute_invalidated?, #banzai_render_context, #cached_html_for, #cached_html_up_to_date?, #can_cache_field?, #invalidated_markdown_cache?, #latest_cached_markdown_version, #local_version, #mentionable_attributes_changed?, #parent_user, #refresh_markdown_cache, #refresh_markdown_cache!, #rendered_field_content, #skip_project_check?, #store_mentions!, #updated_cached_html_for
Methods included from AtomicInternalId
group_init, #internal_id_read_scope, #internal_id_scope_attrs, #internal_id_scope_usage, project_init, scope_attrs, scope_usage
Methods inherited from ApplicationRecord
cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, pluck_primary_key, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order
Methods included from SensitiveSerializableHash
Class Method Details
.link_reference_pattern ⇒ Object
94 95 96 |
# File 'app/models/milestone.rb', line 94 def self.link_reference_pattern @link_reference_pattern ||= super("milestones", /(?<milestone>\d+)/) end |
.min_chars_for_partial_matching ⇒ Object
67 68 69 |
# File 'app/models/milestone.rb', line 67 def self.min_chars_for_partial_matching 2 end |
.reference_pattern ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'app/models/milestone.rb', line 75 def self.reference_pattern # NOTE: The iid pattern only matches when all characters on the expression # are digits, so it will match %2 but not %2.1 because that's probably a # milestone name and we want it to be matched as such. @reference_pattern ||= %r{ (#{Project.reference_pattern})? #{Regexp.escape(reference_prefix)} (?: (?<milestone_iid> \d+(?!\S\w)\b # Integer-based milestone iid, or ) | (?<milestone_name> [^"\s\<]+\b | # String-based single-word milestone title, or "[^"]+" # String-based multi-word milestone surrounded in quotes ) ) }x end |
.reference_prefix ⇒ Object
71 72 73 |
# File 'app/models/milestone.rb', line 71 def self.reference_prefix '%' end |
.search_title(query) ⇒ Object
Searches for timeboxes with a matching title.
This method uses ILIKE on PostgreSQL
query - The search query as a String
Returns an ActiveRecord::Relation.
63 64 65 |
# File 'app/models/milestone.rb', line 63 def self.search_title(query) fuzzy_search(query, [:title]) end |
.sort_by_attribute(method) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'app/models/milestone.rb', line 113 def self.sort_by_attribute(method) sorted = case method.to_s when 'due_date_asc' reorder_by_due_date_asc when 'due_date_desc' reorder(arel_table[:due_date].desc.nulls_last) when 'name_asc' reorder(Arel::Nodes::Ascending.new(arel_table[:title].lower)) when 'name_desc' reorder(Arel::Nodes::Descending.new(arel_table[:title].lower)) when 'start_date_asc' reorder(arel_table[:start_date].asc.nulls_last) when 'start_date_desc' reorder(arel_table[:start_date].desc.nulls_last) else order_by(method) end sorted.with_order_id_desc end |
.sort_with_expired_last(method) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'app/models/milestone.rb', line 135 def self.sort_with_expired_last(method) # NOTE: this is a custom ordering of milestones # to prioritize displaying non-expired milestones and milestones without due dates sorted = reorder(Arel.sql("(CASE WHEN due_date IS NULL THEN 1 WHEN due_date >= CURRENT_DATE THEN 0 ELSE 2 END) ASC")) sorted = if method.to_s == 'expired_last_due_date_desc' sorted.order(due_date: :desc) else sorted.order(due_date: :asc) end sorted.with_order_id_desc end |
.states_count(projects, groups = nil) ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'app/models/milestone.rb', line 148 def self.states_count(projects, groups = nil) return STATE_COUNT_HASH unless projects || groups counts = Milestone .for_projects_and_groups(projects, groups) .reorder(nil) .group(:state) .count { opened: counts['active'] || 0, closed: counts['closed'] || 0, all: counts.values.sum } end |
.upcoming_ids(projects, groups) ⇒ Object
98 99 100 101 102 103 |
# File 'app/models/milestone.rb', line 98 def self.upcoming_ids(projects, groups) unscoped .for_projects_and_groups(projects, groups) .active.where('milestones.due_date > CURRENT_DATE') .order(:project_id, :group_id, :due_date).select('DISTINCT ON (project_id, group_id) id') end |
.with_web_entity_associations ⇒ Object
105 106 107 |
# File 'app/models/milestone.rb', line 105 def self.with_web_entity_associations preload(:group, project: [:project_feature, group: [:parent], namespace: :route]) end |
Instance Method Details
#author_id ⇒ Object
172 173 174 |
# File 'app/models/milestone.rb', line 172 def nil end |
#can_be_closed? ⇒ Boolean
168 169 170 |
# File 'app/models/milestone.rb', line 168 def can_be_closed? active? && issues.opened.count == 0 end |
#for_display ⇒ Object
164 165 166 |
# File 'app/models/milestone.rb', line 164 def for_display self end |
#parent ⇒ Object
184 185 186 187 188 189 190 |
# File 'app/models/milestone.rb', line 184 def parent if group_milestone? group else project end end |
#participants ⇒ Object
109 110 111 |
# File 'app/models/milestone.rb', line 109 def participants User.joins(assigned_issues: :milestone).where(milestones: { id: id }).distinct end |
#subgroup_milestone? ⇒ Boolean
192 193 194 |
# File 'app/models/milestone.rb', line 192 def subgroup_milestone? group_milestone? && parent.subgroup? end |