jirametrics 2.20.1 → 2.20.2pre6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of jirametrics might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/jirametrics/board.rb +4 -0
- data/lib/jirametrics/board_config.rb +2 -1
- data/lib/jirametrics/change_item.rb +9 -3
- data/lib/jirametrics/chart_base.rb +23 -0
- data/lib/jirametrics/cycletime_config.rb +4 -5
- data/lib/jirametrics/daily_wip_chart.rb +1 -1
- data/lib/jirametrics/data_quality_report.rb +2 -0
- data/lib/jirametrics/fix_version.rb +13 -0
- data/lib/jirametrics/html_report_config.rb +3 -1
- data/lib/jirametrics/issue.rb +76 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 63b99b8edec3e017c9ef1be6ad6d7e9c4fa94038126a4fb84cf8d90face9ed9e
|
|
4
|
+
data.tar.gz: c2df48cad225aa8c270dfd3c8f04dc049d3c48642eaa971b885258d58f230a61
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 50dcdb60c3626a3faa6260313fed8a015f24eb757b9f7f338218f7e789b5d0be242e390d9e56b51b2f2f7289eebe6d64df2e195af04a1ecec8acd2fff782772b
|
|
7
|
+
data.tar.gz: 6608ebbefbd28943fed9ab39fa8611a0948e523f76f9f3b37777fdef0a7f0d001df8db2ef4d7d936c588077ba45f27639e8273af279e74291fdbfa638941a9e9
|
data/lib/jirametrics/board.rb
CHANGED
|
@@ -24,7 +24,8 @@ class BoardConfig
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
@board.cycletime = CycleTimeConfig.new(
|
|
27
|
-
|
|
27
|
+
possible_statuses: project_config.possible_statuses,
|
|
28
|
+
label: label, block: block, file_system: project_config.file_system,
|
|
28
29
|
settings: project_config.settings
|
|
29
30
|
)
|
|
30
31
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class ChangeItem
|
|
4
|
-
attr_reader :field, :value_id, :old_value_id, :raw, :author_raw
|
|
4
|
+
attr_reader :field, :value_id, :old_value_id, :raw, :author_raw, :field_id
|
|
5
5
|
attr_accessor :value, :old_value, :time
|
|
6
6
|
|
|
7
7
|
def initialize raw:, author_raw:, time:, artificial: false
|
|
@@ -13,9 +13,15 @@ class ChangeItem
|
|
|
13
13
|
|
|
14
14
|
@field = @raw['field']
|
|
15
15
|
@value = @raw['toString']
|
|
16
|
-
@value_id = @raw['to'].to_i
|
|
17
16
|
@old_value = @raw['fromString']
|
|
18
|
-
|
|
17
|
+
if sprint?
|
|
18
|
+
@value_id = @raw['to'].split(', ').collect(&:to_i)
|
|
19
|
+
@old_value_id = (@raw['from'] || '').split(', ').collect(&:to_i)
|
|
20
|
+
else
|
|
21
|
+
@value_id = @raw['to'].to_i
|
|
22
|
+
@old_value_id = @raw['from']&.to_i
|
|
23
|
+
end
|
|
24
|
+
@field_id = @raw['fieldId']
|
|
19
25
|
@artificial = artificial
|
|
20
26
|
end
|
|
21
27
|
|
|
@@ -22,6 +22,14 @@ class ChartBase
|
|
|
22
22
|
@canvas_responsive = true
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def call_before_run &proc
|
|
26
|
+
(@call_before_run_procs ||= []) << proc
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def before_run
|
|
30
|
+
@call_before_run_procs&.each { |proc| proc.call }
|
|
31
|
+
end
|
|
32
|
+
|
|
25
33
|
def aggregated_project?
|
|
26
34
|
@aggregated_project
|
|
27
35
|
end
|
|
@@ -279,4 +287,19 @@ class ChartBase
|
|
|
279
287
|
</div>
|
|
280
288
|
TEXT
|
|
281
289
|
end
|
|
290
|
+
|
|
291
|
+
# Set a cycletime for just this one chart, overriding the one for the report.
|
|
292
|
+
def cycletime &block
|
|
293
|
+
call_before_run do
|
|
294
|
+
@cycletime = CycleTimeConfig.new(
|
|
295
|
+
possible_statuses: possible_statuses, label: nil, block: block, file_system: file_system,
|
|
296
|
+
settings: settings
|
|
297
|
+
)
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Returns the cycletime in use right now, which may be specific to the chart or across the report.
|
|
302
|
+
def cycletime_for_issue issue
|
|
303
|
+
@cycletime || issue.board.cycletime
|
|
304
|
+
end
|
|
282
305
|
end
|
|
@@ -6,15 +6,14 @@ require 'date'
|
|
|
6
6
|
class CycleTimeConfig
|
|
7
7
|
include SelfOrIssueDispatcher
|
|
8
8
|
|
|
9
|
-
attr_reader :label, :
|
|
9
|
+
attr_reader :label, :possible_statuses, :settings, :file_system
|
|
10
10
|
|
|
11
|
-
def initialize
|
|
11
|
+
def initialize possible_statuses:, label:, block:, settings:, file_system: nil, today: Date.today
|
|
12
12
|
|
|
13
|
-
@
|
|
13
|
+
@possible_statuses = possible_statuses
|
|
14
14
|
@label = label
|
|
15
15
|
@today = today
|
|
16
16
|
@settings = settings
|
|
17
|
-
@cache_cycletime_calculations = settings['cache_cycletime_calculations']
|
|
18
17
|
|
|
19
18
|
# If we hit something deprecated and this is nil then we'll blow up. Although it's ugly, this
|
|
20
19
|
# may make it easier to find problems in the test code ;-)
|
|
@@ -68,7 +67,7 @@ class CycleTimeConfig
|
|
|
68
67
|
def started_stopped_changes issue
|
|
69
68
|
cache_key = "#{issue.key}:#{issue.board.id}"
|
|
70
69
|
last_result = (@cache ||= {})[cache_key]
|
|
71
|
-
return *last_result if last_result &&
|
|
70
|
+
return *last_result if last_result && settings['cache_cycletime_calculations']
|
|
72
71
|
|
|
73
72
|
started = @start_at.call(issue)
|
|
74
73
|
stopped = @stop_at.call(issue)
|
|
@@ -66,7 +66,7 @@ class DailyWipChart < ChartBase
|
|
|
66
66
|
hash = {}
|
|
67
67
|
|
|
68
68
|
@issues.each do |issue|
|
|
69
|
-
start, stop = issue.
|
|
69
|
+
start, stop = cycletime_for_issue(issue).started_stopped_dates(issue)
|
|
70
70
|
next if start.nil? && stop.nil?
|
|
71
71
|
|
|
72
72
|
# If it stopped but never started then assume it started at creation so the data points
|
|
@@ -266,6 +266,8 @@ class DataQualityReport < ChartBase
|
|
|
266
266
|
|
|
267
267
|
def scan_for_items_blocked_on_closed_tickets entry:
|
|
268
268
|
entry.issue.issue_links.each do |link|
|
|
269
|
+
next unless settings['blocked_link_text'].include?(link.label)
|
|
270
|
+
|
|
269
271
|
this_active = !entry.stopped
|
|
270
272
|
other_active = !link.other_issue.board.cycletime.started_stopped_times(link.other_issue).last
|
|
271
273
|
next unless this_active && !other_active
|
|
@@ -11,11 +11,24 @@ class FixVersion
|
|
|
11
11
|
@raw['name']
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
def description
|
|
15
|
+
@raw['description']
|
|
16
|
+
end
|
|
17
|
+
|
|
14
18
|
def id
|
|
15
19
|
@raw['id'].to_i
|
|
16
20
|
end
|
|
17
21
|
|
|
22
|
+
def release_date
|
|
23
|
+
text = @raw['releaseDate']
|
|
24
|
+
text.nil? ? nil : Date.parse(text)
|
|
25
|
+
end
|
|
26
|
+
|
|
18
27
|
def released?
|
|
19
28
|
@raw['released']
|
|
20
29
|
end
|
|
30
|
+
|
|
31
|
+
def archived?
|
|
32
|
+
@raw['archived']
|
|
33
|
+
end
|
|
21
34
|
end
|
|
@@ -52,7 +52,8 @@ class HtmlReportConfig
|
|
|
52
52
|
raise 'Multiple cycletimes not supported' if board.cycletime
|
|
53
53
|
|
|
54
54
|
board.cycletime = CycleTimeConfig.new(
|
|
55
|
-
|
|
55
|
+
possible_statuses: file_config.project_config, label: label, block: block,
|
|
56
|
+
file_system: file_system, settings: settings
|
|
56
57
|
)
|
|
57
58
|
end
|
|
58
59
|
end
|
|
@@ -175,6 +176,7 @@ class HtmlReportConfig
|
|
|
175
176
|
after_init_block&.call chart
|
|
176
177
|
|
|
177
178
|
@charts << chart
|
|
179
|
+
chart.before_run
|
|
178
180
|
html chart.run
|
|
179
181
|
end
|
|
180
182
|
|
data/lib/jirametrics/issue.rb
CHANGED
|
@@ -212,8 +212,79 @@ class Issue
|
|
|
212
212
|
first_time_in_status(*board.visible_columns.collect(&:status_ids).flatten)
|
|
213
213
|
end
|
|
214
214
|
|
|
215
|
+
# If this issue will ever be in an active sprint then return the time that it
|
|
216
|
+
# was first added to that sprint, whether or not the sprint was active at that
|
|
217
|
+
# time. Although it seems like an odd thing to calculate, it's a reasonable proxy
|
|
218
|
+
# for 'ready' in cases where the team doesn't have an explicit 'ready' status.
|
|
219
|
+
# You'd be better off with an explicit 'ready' but sometimes that's not an option.
|
|
220
|
+
def first_time_added_to_active_sprint
|
|
221
|
+
unless board.scrum?
|
|
222
|
+
raise 'first_time_added_to_active_sprint() can only be used with Scrum boards: ' \
|
|
223
|
+
"issue=#{key}, board=#{board.inspect}"
|
|
224
|
+
end
|
|
225
|
+
data_clazz = Struct.new(:sprint_id, :sprint_start, :sprint_stop, :change)
|
|
226
|
+
|
|
227
|
+
matching_changes = []
|
|
228
|
+
all_datas = []
|
|
229
|
+
|
|
230
|
+
@changes.each do |change|
|
|
231
|
+
next unless change.sprint?
|
|
232
|
+
|
|
233
|
+
added_sprint_ids = change.value_id - change.old_value_id
|
|
234
|
+
added_sprint_ids.each do |id|
|
|
235
|
+
data = data_clazz.new
|
|
236
|
+
data.sprint_id = id
|
|
237
|
+
data.change = change
|
|
238
|
+
data.sprint_start, data.sprint_stop = find_sprint_start_end(sprint_id: id, change: change)
|
|
239
|
+
all_datas << data
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
removed_sprint_ids = change.old_value_id - change.value_id
|
|
243
|
+
removed_sprint_ids.each do |id|
|
|
244
|
+
data = all_datas.find { |d| d.sprint_id == id }
|
|
245
|
+
|
|
246
|
+
all_datas.delete(data)
|
|
247
|
+
|
|
248
|
+
next if data.sprint_start.nil? || data.sprint_start >= change.time
|
|
249
|
+
|
|
250
|
+
matching_changes << data.change
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# There can't be any more removes so whatever is left is a valid option
|
|
255
|
+
# Now all we care about is if the sprint has started.
|
|
256
|
+
all_datas.each do |data|
|
|
257
|
+
matching_changes << data.change if data.sprint_start
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
matching_changes.min_by(&:time)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def find_sprint_start_end sprint_id:, change:
|
|
264
|
+
start = nil
|
|
265
|
+
stop = nil
|
|
266
|
+
|
|
267
|
+
sprint = board.sprints.find { |s| s.id == sprint_id }
|
|
268
|
+
if sprint
|
|
269
|
+
start = sprint.start_time
|
|
270
|
+
stop = sprint.completed_time
|
|
271
|
+
puts "#{key}: Found sprint. values: #{start}/#{stop} from sprints file"
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
sprint_data = raw['fields'][change.field_id].find { |sd| sd['id'].to_i == sprint_id }
|
|
275
|
+
if sprint_data
|
|
276
|
+
start = parse_time(sprint_data['startDate'])
|
|
277
|
+
stop = parse_time(sprint_data['completeDate'])
|
|
278
|
+
puts "#{key}: Found sprint. values: #{start}/#{stop} from field: #{change.field_id}"
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
[start, stop]
|
|
282
|
+
end
|
|
283
|
+
|
|
215
284
|
def parse_time text
|
|
216
|
-
if text.
|
|
285
|
+
if text.nil?
|
|
286
|
+
nil
|
|
287
|
+
elsif text.is_a? String
|
|
217
288
|
Time.parse(text).getlocal(@timezone_offset)
|
|
218
289
|
else
|
|
219
290
|
Time.at(text / 1000).getlocal(@timezone_offset)
|
|
@@ -225,6 +296,10 @@ class Issue
|
|
|
225
296
|
parse_time @raw['fields']['created'] if @raw['fields']['created']
|
|
226
297
|
end
|
|
227
298
|
|
|
299
|
+
def time_created
|
|
300
|
+
@changes.first
|
|
301
|
+
end
|
|
302
|
+
|
|
228
303
|
def updated
|
|
229
304
|
parse_time @raw['fields']['updated']
|
|
230
305
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jirametrics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.20.
|
|
4
|
+
version: 2.20.2pre6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Bowler
|
|
@@ -159,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
159
159
|
- !ruby/object:Gem::Version
|
|
160
160
|
version: '0'
|
|
161
161
|
requirements: []
|
|
162
|
-
rubygems_version:
|
|
162
|
+
rubygems_version: 4.0.3
|
|
163
163
|
specification_version: 4
|
|
164
164
|
summary: Extract Jira metrics
|
|
165
165
|
test_files: []
|