Class: BlockedStalledChangeStreamBuilder
- Inherits:
-
Object
- Object
- BlockedStalledChangeStreamBuilder
- Defined in:
- lib/jirametrics/blocked_stalled_change_stream_builder.rb
Overview
Builds the time-ordered stream of BlockedStalledChange entries for a single issue. Everything it needs is passed in explicitly (changes, settings, and a few resolved values) so it never reaches back through the issue's collaborators.
Defined Under Namespace
Classes: BlockingState
Instance Method Summary collapse
- #apply_link_change(change, blocking_issue_keys) ⇒ Object
- #build(end_time:) ⇒ Object
- #check_for_stalled(change_time:, previous_change_time:, stalled_threshold:, blocking_stalled_changes:) ⇒ Object
- #comment_near(flag_time) ⇒ Object
- #finalize_stalled_tail(result) ⇒ Object
- #flag_logic(change) ⇒ Object
- #flag_reason_from_comment(change) ⇒ Object
- #flagged_means_blocked? ⇒ Boolean
-
#initialize(changes:, settings:, created:, key:, subtask_activity_times:, atlassian_document_format:) ⇒ BlockedStalledChangeStreamBuilder
constructor
A new instance of BlockedStalledChangeStreamBuilder.
- #insert_stalled_changes(ranges, stalled_threshold_seconds, blocking_stalled_changes) ⇒ Object
- #new_blocked_stalled_change(state, time) ⇒ Object
- #record_change?(candidate, previous_was_active, change, mock_change) ⇒ Boolean
- #split_range_by_subtask_activity(initial_range) ⇒ Object
- #status_change(change) ⇒ Object
- #update_blocking_state(state, change) ⇒ Object
Constructor Details
#initialize(changes:, settings:, created:, key:, subtask_activity_times:, atlassian_document_format:) ⇒ BlockedStalledChangeStreamBuilder
Returns a new instance of BlockedStalledChangeStreamBuilder.
14 15 16 17 18 19 20 21 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 14 def initialize changes:, settings:, created:, key:, subtask_activity_times:, atlassian_document_format: @changes = changes @settings = settings @created = created @key = key @subtask_activity_times = subtask_activity_times @atlassian_document_format = atlassian_document_format end |
Instance Method Details
#apply_link_change(change, blocking_issue_keys) ⇒ Object
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 96 def apply_link_change change, blocking_issue_keys link_value = change.value || change.old_value # Example: "This issue is satisfied by ANON-30465" unless /^This (?<_>issue|work item) (?<link_text>.+) (?<issue_key>.+)$/ =~ link_value puts "Issue(#{@key}) Can't parse link text: #{link_value}" return end return unless @settings['blocked_link_text'].include? link_text if change.value blocking_issue_keys << issue_key else blocking_issue_keys.delete issue_key end end |
#build(end_time:) ⇒ Object
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 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 23 def build end_time: result = [] state = BlockingState.new(flag: nil, flag_reason: nil, status: nil, is_blocked: false, blocking_issue_keys: []) previous_was_active = false # Must start as false so that the creation will insert an :active previous_change_time = @created # This mock change is to force the writing of one last entry at the end of the time range. # By doing this, we're able to eliminate a lot of duplicated code in charts. mock_change = ChangeItem.new time: end_time, artificial: true, raw: { 'field' => '' }, author_raw: nil (@changes + [mock_change]).each do |change| previous_was_active = false if check_for_stalled( change_time: change.time, previous_change_time: previous_change_time, stalled_threshold: @settings['stalled_threshold_days'], blocking_stalled_changes: result ) update_blocking_state state, change new_change = new_blocked_stalled_change state, change.time # We don't want to dump two actives in a row as that would just be noise. Unless this is # the mock change, which we always want to dump result << new_change if record_change? new_change, previous_was_active, change, mock_change previous_was_active = new_change.active? previous_change_time = change.time end finalize_stalled_tail result result end |
#check_for_stalled(change_time:, previous_change_time:, stalled_threshold:, blocking_stalled_changes:) ⇒ Object
156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 156 def check_for_stalled change_time:, previous_change_time:, stalled_threshold:, blocking_stalled_changes: stalled_threshold_seconds = stalled_threshold * 60 * 60 * 24 # The most common case will be nothing to split so quick escape. return false if (change_time - previous_change_time).to_i < stalled_threshold_seconds # If the last identified change was blocked then it doesn't matter now long we've waited, we're still blocked. return false if blocking_stalled_changes[-1]&.blocked? ranges = split_range_by_subtask_activity previous_change_time..change_time insert_stalled_changes ranges, stalled_threshold_seconds, blocking_stalled_changes end |
#comment_near(flag_time) ⇒ Object
128 129 130 131 132 133 134 135 136 137 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 128 def comment_near flag_time # When the user is adding a comment to explain why a flag was set, the flag is set immediately # and the comment is inserted after the user hits enter, which means that there is some time # gap. If a comment happened shortly after the flag was set, we assume they're linked. This # won't always be true and so there will be false positives, but it's a reasonable assumption. max_seconds_between_flag_and_comment = 30 @changes.find do |c| c.comment? && c.time >= flag_time && (c.time - flag_time) <= max_seconds_between_flag_and_comment end end |
#finalize_stalled_tail(result) ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 139 def finalize_stalled_tail result return unless result.size >= 2 # The existence of the mock entry will mess with the stalled count as it will wake everything # back up. This hack will clean up appropriately. hack = result.pop result << BlockedStalledChange.new( flagged: hack.flag, flag_reason: hack.flag_reason, status: hack.status, status_is_blocking: hack.status_is_blocking, blocking_issue_keys: hack.blocking_issue_keys, time: hack.time, stalled_days: result[-1].stalled_days ) end |
#flag_logic(change) ⇒ Object
113 114 115 116 117 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 113 def flag_logic change flag = change.value flag = nil if change.value == '' [flag, flag ? flag_reason_from_comment(change) : nil] end |
#flag_reason_from_comment(change) ⇒ Object
119 120 121 122 123 124 125 126 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 119 def flag_reason_from_comment change comment_change = comment_near change.time flag_reason = comment_change && @atlassian_document_format.to_text(comment_change.value) # Newer Jira instances may add this extra text but older instances did not. Strip it out if found. flag_reason = flag_reason&.sub(/\A:flag_on: Flag added\s*/m, '')&.strip flag_reason = nil if flag_reason && flag_reason.empty? flag_reason end |
#flagged_means_blocked? ⇒ Boolean
67 68 69 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 67 def flagged_means_blocked? !!@settings['flagged_means_blocked'] end |
#insert_stalled_changes(ranges, stalled_threshold_seconds, blocking_stalled_changes) ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 182 def insert_stalled_changes ranges, stalled_threshold_seconds, blocking_stalled_changes inserted_stalled = false ranges.sort_by(&:begin).each do |range| seconds = (range.end - range.begin).to_i next if seconds < stalled_threshold_seconds an_hour_later = range.begin + (60 * 60) blocking_stalled_changes << BlockedStalledChange.new(stalled_days: seconds / (24 * 60 * 60), time: an_hour_later) inserted_stalled = true end inserted_stalled end |
#new_blocked_stalled_change(state, time) ⇒ Object
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 71 def new_blocked_stalled_change state, time BlockedStalledChange.new( flagged: state.flag, flag_reason: state.flag_reason, status: state.status, status_is_blocking: state.status.nil? || state.is_blocked, blocking_issue_keys: (state.blocking_issue_keys.empty? ? nil : state.blocking_issue_keys.dup), time: time ) end |
#record_change?(candidate, previous_was_active, change, mock_change) ⇒ Boolean
82 83 84 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 82 def record_change? candidate, previous_was_active, change, mock_change !candidate.active? || !previous_was_active || change == mock_change end |
#split_range_by_subtask_activity(initial_range) ⇒ Object
169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 169 def split_range_by_subtask_activity initial_range list = [initial_range] @subtask_activity_times.each do |time| matching_range = list.find { |range| time.between?(range.begin, range.end) } next unless matching_range list.delete matching_range list << (matching_range.begin..time) list << (time..matching_range.end) end list end |
#status_change(change) ⇒ Object
86 87 88 89 90 91 92 93 94 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 86 def status_change change if @settings['blocked_statuses'].find_by_id(change.value_id) [change.value, true] elsif @settings['stalled_statuses'].find_by_id(change.value_id) [change.value, false] else [nil, false] end end |
#update_blocking_state(state, change) ⇒ Object
57 58 59 60 61 62 63 64 65 |
# File 'lib/jirametrics/blocked_stalled_change_stream_builder.rb', line 57 def update_blocking_state state, change if change.flagged? && flagged_means_blocked? state.flag, state.flag_reason = flag_logic change elsif change.status? state.status, state.is_blocked = status_change change elsif change.link? apply_link_change change, state.blocking_issue_keys end end |