jirametrics 2.13 → 2.14
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.
- checksums.yaml +4 -4
- data/lib/jirametrics/atlassian_document_format.rb +6 -2
- data/lib/jirametrics/board_config.rb +1 -0
- data/lib/jirametrics/chart_base.rb +2 -0
- data/lib/jirametrics/daily_view.rb +50 -29
- data/lib/jirametrics/html/index.css +5 -0
- data/lib/jirametrics/issue.rb +2 -3
- data/lib/jirametrics/project_config.rb +20 -3
- 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: d5b1b9e8d837f6d74990d377db007ed5e55670de77738ab38a04c6d023d865c3
|
4
|
+
data.tar.gz: 99e8ef3e85a3dfa2bd6d845a32d974718c0e9884d2f4750891ba491fd884ab0b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 804a25c8a19df9ae9862e2f95539183ece53e3841e590c64b8deb7048601bfb2b42ad5b75c075b85058abb7cea0cc875d517f4208bd5edbf98e2129e5567af59
|
7
|
+
data.tar.gz: '0059cde9746423c5baf3ca1f47243b6489ccf77b8fd409255f7301f638eb1975b8b2815d266f720cd9f1cd51e2cf0bc9e33cbad3e34110ca1dedc4ad7fc9ff5b'
|
@@ -5,6 +5,7 @@ class AtlassianDocumentFormat
|
|
5
5
|
|
6
6
|
def initialize users:, timezone_offset:
|
7
7
|
@users = users
|
8
|
+
@timezone_offset = timezone_offset
|
8
9
|
end
|
9
10
|
|
10
11
|
def to_html input
|
@@ -14,14 +15,17 @@ class AtlassianDocumentFormat
|
|
14
15
|
.gsub(/\[~accountid:([^\]]+)\]/) { expand_account_id $1 } # Tagged people
|
15
16
|
.gsub(/\[([^\|]+)\|(https?[^\]]+)\]/, '<a href="\2">\1</a>') # URLs
|
16
17
|
.gsub("\n", '<br />')
|
17
|
-
|
18
|
+
elsif input['content']
|
18
19
|
input['content'].collect { |element| adf_node_to_html element }.join("\n")
|
20
|
+
else
|
21
|
+
# We have an actual ADF document with no content.
|
22
|
+
''
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
22
26
|
# ADF is Atlassian Document Format
|
23
27
|
# https://developer.atlassian.com/cloud/jira/platform/apis/document/structure/
|
24
|
-
def adf_node_to_html node
|
28
|
+
def adf_node_to_html node # rubocop:disable Metrics/CyclomaticComplexity
|
25
29
|
closing_tag = nil
|
26
30
|
node_attrs = node['attrs']
|
27
31
|
|
@@ -11,6 +11,7 @@ class BoardConfig
|
|
11
11
|
|
12
12
|
def run
|
13
13
|
@board = @project_config.all_boards[id]
|
14
|
+
raise "Can't find board #{id.inspect} in #{@project_config.all_boards.keys.inspect}" unless @board
|
14
15
|
|
15
16
|
instance_eval(&@block)
|
16
17
|
raise "Must specify a cycletime for board #{@id}" if @board.cycletime.nil?
|
@@ -82,7 +82,7 @@ class DailyView < ChartBase
|
|
82
82
|
blocked_stalled = issue.blocked_stalled_by_date(
|
83
83
|
date_range: today..today, chart_end_time: time_range.end, settings: settings
|
84
84
|
)[today]
|
85
|
-
return []
|
85
|
+
return [] if blocked_stalled.active?
|
86
86
|
|
87
87
|
lines = []
|
88
88
|
if blocked_stalled.blocked?
|
@@ -102,15 +102,18 @@ class DailyView < ChartBase
|
|
102
102
|
lines
|
103
103
|
end
|
104
104
|
|
105
|
-
def make_issue_label issue
|
106
|
-
"<img src='#{issue.type_icon_url}' title='#{issue.type}' class='icon' /> "
|
107
|
-
|
105
|
+
def make_issue_label issue:, done:
|
106
|
+
label = "<img src='#{issue.type_icon_url}' title='#{issue.type}' class='icon' /> "
|
107
|
+
label << '<s>' if done
|
108
|
+
label << "<b><a href='#{issue.url}'>#{issue.key}</a></b> <i>#{issue.summary}</i>"
|
109
|
+
label << '</s>' if done
|
110
|
+
label
|
108
111
|
end
|
109
112
|
|
110
|
-
def make_title_line issue
|
113
|
+
def make_title_line issue:, done:
|
111
114
|
title_line = +''
|
112
115
|
title_line << color_block('--expedited-color', title: 'Expedited') if issue.expedited?
|
113
|
-
title_line << make_issue_label(issue)
|
116
|
+
title_line << make_issue_label(issue: issue, done: done)
|
114
117
|
title_line
|
115
118
|
end
|
116
119
|
|
@@ -119,20 +122,25 @@ class DailyView < ChartBase
|
|
119
122
|
parent_key = issue.parent_key
|
120
123
|
if parent_key
|
121
124
|
parent = issues.find_by_key key: parent_key, include_hidden: true
|
122
|
-
text = parent ? make_issue_label(parent) : parent_key
|
125
|
+
text = parent ? make_issue_label(issue: parent, done: parent.done?) : parent_key
|
123
126
|
lines << ["Parent: #{text}"]
|
124
127
|
end
|
125
128
|
lines
|
126
129
|
end
|
127
130
|
|
128
|
-
def make_stats_lines issue
|
131
|
+
def make_stats_lines issue:, done:
|
129
132
|
line = []
|
130
133
|
|
131
134
|
line << "<img src='#{issue.priority_url}' class='icon' /> <b>#{issue.priority_name}</b>"
|
132
135
|
|
133
|
-
|
134
|
-
|
136
|
+
if done
|
137
|
+
cycletime = issue.board.cycletime.cycletime(issue)
|
135
138
|
|
139
|
+
line << "Cycletime: <b>#{label_days cycletime}</b>"
|
140
|
+
else
|
141
|
+
age = issue.board.cycletime.age(issue, today: date_range.end)
|
142
|
+
line << "Age: <b>#{age ? label_days(age) : '(Not Started)'}</b>"
|
143
|
+
end
|
136
144
|
line << "Status: <b>#{format_status issue.status, board: issue.board}</b>"
|
137
145
|
|
138
146
|
column = issue.board.visible_columns.find { |c| c.status_ids.include?(issue.status.id) }
|
@@ -158,13 +166,20 @@ class DailyView < ChartBase
|
|
158
166
|
|
159
167
|
def make_child_lines issue
|
160
168
|
lines = []
|
161
|
-
subtasks = issue.subtasks
|
169
|
+
subtasks = issue.subtasks
|
170
|
+
|
171
|
+
return lines if subtasks.empty?
|
172
|
+
|
173
|
+
id = next_id
|
174
|
+
lines <<
|
175
|
+
"<a href=\"javascript:toggle_visibility('open#{id}', 'close#{id}', 'section#{id}');\">" \
|
176
|
+
"<span id='open#{id}' style='display: none'>▶ Child issues</span>" \
|
177
|
+
"<span id='close#{id}'>▼ Child issues</span></a>"
|
178
|
+
lines << "<section id='section#{id}'>"
|
179
|
+
|
180
|
+
lines += subtasks
|
181
|
+
lines << '</section>'
|
162
182
|
|
163
|
-
unless subtasks.empty?
|
164
|
-
icon_urls = subtasks.collect(&:type_icon_url).uniq.collect { |url| "<img src='#{url}' class='icon' />" }
|
165
|
-
lines << (icon_urls << 'Incomplete child issues')
|
166
|
-
lines += subtasks
|
167
|
-
end
|
168
183
|
lines
|
169
184
|
end
|
170
185
|
|
@@ -196,19 +211,19 @@ class DailyView < ChartBase
|
|
196
211
|
end
|
197
212
|
|
198
213
|
def history_text change:, board:
|
214
|
+
convertor = ->(value, _id) { value.inspect }
|
215
|
+
convertor = ->(_value, id) { format_status(board.possible_statuses.find_by_id(id), board: board) } if change.status?
|
216
|
+
|
199
217
|
if change.comment? || change.description?
|
200
218
|
atlassian_document_format.to_html(change.value)
|
201
|
-
elsif change.
|
202
|
-
|
203
|
-
to = convertor.call(change.value_id)
|
219
|
+
elsif %w[status priority assignee duedate issuetype].include?(change.field)
|
220
|
+
to = convertor.call(change.value, change.value_id)
|
204
221
|
if change.old_value
|
205
|
-
from = convertor.call(change.old_value_id)
|
222
|
+
from = convertor.call(change.old_value, change.old_value_id)
|
206
223
|
"Changed from #{from} to #{to}"
|
207
224
|
else
|
208
225
|
"Set to #{to}"
|
209
226
|
end
|
210
|
-
elsif %w[priority assignee duedate issuetype].include?(change.field)
|
211
|
-
"Changed from \"#{change.old_value}\" to \"#{change.value}\""
|
212
227
|
elsif change.flagged?
|
213
228
|
change.value == '' ? 'Off' : 'On'
|
214
229
|
else
|
@@ -242,15 +257,19 @@ class DailyView < ChartBase
|
|
242
257
|
end
|
243
258
|
|
244
259
|
def assemble_issue_lines issue, child:
|
260
|
+
done = issue.done?
|
261
|
+
|
245
262
|
lines = []
|
246
|
-
lines << [make_title_line(issue)]
|
263
|
+
lines << [make_title_line(issue: issue, done: done)]
|
247
264
|
lines += make_parent_lines(issue) unless child
|
248
|
-
lines += make_stats_lines(issue)
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
265
|
+
lines += make_stats_lines(issue: issue, done: done)
|
266
|
+
unless done
|
267
|
+
lines += make_description_lines(issue)
|
268
|
+
lines += make_sprints_lines(issue)
|
269
|
+
lines += make_blocked_stalled_lines(issue)
|
270
|
+
lines += make_child_lines(issue)
|
271
|
+
lines += make_history_lines(issue)
|
272
|
+
end
|
254
273
|
lines
|
255
274
|
end
|
256
275
|
|
@@ -261,6 +280,8 @@ class DailyView < ChartBase
|
|
261
280
|
assemble_issue_lines(issue, child: child).each do |row|
|
262
281
|
if row.is_a? Issue
|
263
282
|
result << render_issue(row, child: true)
|
283
|
+
elsif row.is_a?(String)
|
284
|
+
result << row
|
264
285
|
else
|
265
286
|
result << '<div class="heading">'
|
266
287
|
row.each do |chunk|
|
data/lib/jirametrics/issue.rb
CHANGED
@@ -681,9 +681,8 @@ class Issue
|
|
681
681
|
def done?
|
682
682
|
if artificial? || board.cycletime.nil?
|
683
683
|
# This was probably loaded as a linked issue, which means we don't know what board it really
|
684
|
-
# belonged to. The best we can do is look at the status
|
685
|
-
|
686
|
-
status.category.name == 'Done'
|
684
|
+
# belonged to. The best we can do is look at the status key
|
685
|
+
status.category.done?
|
687
686
|
else
|
688
687
|
board.cycletime.done? self
|
689
688
|
end
|
@@ -114,10 +114,14 @@ class ProjectConfig
|
|
114
114
|
def file_prefix prefix
|
115
115
|
# The file_prefix has to be set before almost everything else. It really should have been an attribute
|
116
116
|
# on the project declaration itself. Hindsight is 20/20.
|
117
|
+
|
118
|
+
# There can only be one of these
|
117
119
|
if @file_prefix
|
118
|
-
raise "file_prefix
|
120
|
+
raise "file_prefix can only be set once. Was #{@file_prefix.inspect} and now changed to #{prefix.inspect}."
|
119
121
|
end
|
120
122
|
|
123
|
+
raise_if_prefix_already_used(prefix)
|
124
|
+
|
121
125
|
@file_prefix = prefix
|
122
126
|
|
123
127
|
# Yes, this is a wierd place to be initializing this. Unfortunately, it has to happen after the file_prefix
|
@@ -130,8 +134,21 @@ class ProjectConfig
|
|
130
134
|
@file_prefix
|
131
135
|
end
|
132
136
|
|
133
|
-
def
|
134
|
-
|
137
|
+
def raise_if_prefix_already_used prefix
|
138
|
+
@exporter.project_configs.each do |project|
|
139
|
+
next unless project.get_file_prefix(raise_if_not_set: false) == prefix && project.target_path == target_path
|
140
|
+
|
141
|
+
raise "Project #{name.inspect} specifies file prefix #{prefix.inspect}, " \
|
142
|
+
"but that is already used by project #{project.name.inspect} in the same target path #{target_path.inspect}. " \
|
143
|
+
'This is almost guaranteed to be too much copy and paste in your configuration. ' \
|
144
|
+
'File prefixes must be unique within a directory.'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def get_file_prefix raise_if_not_set: true
|
149
|
+
if @file_prefix.nil? && raise_if_not_set
|
150
|
+
raise 'file_prefix has not been set yet. Move it to the top of the project declaration.'
|
151
|
+
end
|
135
152
|
|
136
153
|
@file_prefix
|
137
154
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jirametrics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '2.
|
4
|
+
version: '2.14'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Bowler
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-08-18 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: random-word
|