@@ -25,6 +25,7 @@ use utf8;
25
25
use Data::Dumper qw/ Dumper/ ;
26
26
use Env qw( $HOME) ;
27
27
use Config::Simple;
28
+ use GraphViz2;
28
29
use JIRA::REST;
29
30
use Term::ReadLine;
30
31
@@ -161,6 +162,135 @@ sub create {
161
162
return " Created issue $res ->{key}" ;
162
163
}
163
164
165
+ sub status_color {
166
+ my $status = shift ;
167
+
168
+ return ' black' ;
169
+ }
170
+
171
+ sub status_bgcolor {
172
+ my $status = shift ;
173
+
174
+ if ($status eq ' done' ) {
175
+ return ' gray' ;
176
+ } else {
177
+ return ' black' ;
178
+ }
179
+ }
180
+
181
+ sub status_width {
182
+ my $status = shift ;
183
+
184
+ return 2.5 if $status !~ m / (new|done)/ ;
185
+
186
+ return 1.0;
187
+ }
188
+
189
+ sub status_label {
190
+ my ($status , $label ) = @_ ;
191
+
192
+ if ($status ne ' indeterminate' ) {
193
+ return " $label ($status )" ;
194
+ } else {
195
+ return $label
196
+ }
197
+ }
198
+
199
+ sub graph {
200
+ my $iss = shift ;
201
+ my $recursionmap = shift ;
202
+ my $graph = shift ;
203
+
204
+ $iss = $curissue if !defined ($iss );
205
+ return " No issue specified" if !defined ($iss );
206
+
207
+ if (defined $recursionmap && defined $recursionmap -> {$iss }) {
208
+ return ;
209
+ }
210
+
211
+ my $res = eval {
212
+ $jira -> GET(" /issue/$iss " , { expand => ' names' });
213
+ };
214
+ return " Error with getting issue '$iss ': $@ " unless ($res );
215
+
216
+
217
+ $graph = GraphViz2-> new(
218
+ edge => { color => ' grey' },
219
+ global => { directed => 1 },
220
+ graph => { label => " $iss Dependencies" , rankdir => ' LR' },
221
+ node => { shape => ' box' , style => ' rounded,filled' , fontcolor => ' white' },
222
+ ) unless defined $graph ;
223
+
224
+ my $status = ($res -> {fields }-> {status }-> {statusCategory }-> {key });
225
+ my $color = status_color($status );
226
+ my $bgcolor = status_bgcolor($status );
227
+ my $width = status_width($status );
228
+ $graph -> add_node(name => $iss , shape => ' box' , color => $bgcolor , fillcolor => $bgcolor ) unless defined ($recursionmap );
229
+
230
+ my $epic_issues = undef ;
231
+ my $do_recurse = 0;
232
+ if (is_epic($res )) {
233
+ $jql = ' "' . $cfg -> param(' epiclink' ) . ' "="' . $res -> {fields }-> {$customfields_n2i -> {$cfg -> param(' epicname' )}} . ' "' ;
234
+ my $perpage_stash = $perpage ;
235
+ $perpage = 1000;
236
+ $epic_issues = eval { do_search($jql , 0, [ qw/ summary status/ ]) };
237
+ return " Error with search: $@ " unless $epic_issues ;
238
+ $perpage = $perpage_stash ;
239
+ $do_recurse = 1;
240
+ }
241
+
242
+ $recursionmap = { $iss => 1 } unless defined $recursionmap ;
243
+
244
+ foreach my $link (@{$res -> {fields }-> {issuelinks }}) {
245
+ my $outward = defined ($link -> {outwardIssue }) ? 1 : 0;
246
+ my $linktype = $link -> {type }-> {name };
247
+ my $fromkey = ' ' ;
248
+ my $tokey = ' ' ;
249
+ my $key = ' ' ;
250
+ my $status = ' ' ;
251
+
252
+ if ($outward ) {
253
+ $fromkey = $iss ;
254
+ $key = $tokey = $link -> {outwardIssue }-> {key };
255
+ $status = $link -> {outwardIssue }-> {fields }-> {status }-> {statusCategory }-> {key };
256
+ } else {
257
+ $tokey = $iss ;
258
+ $key = $fromkey = $link -> {inwardIssue }-> {key };
259
+ $status = $link -> {inwardIssue }-> {fields }-> {status }-> {statusCategory }-> {key };
260
+ }
261
+
262
+
263
+ $color = status_color($status );
264
+ my $bgcolor = status_bgcolor($status );
265
+ my $width = status_width($status );
266
+ my $label = status_label($status , $link -> {type }-> {outward });
267
+
268
+ $graph -> add_node(name => $key , color => $bgcolor , fillcolor => $bgcolor );
269
+ $graph -> add_edge(from => $fromkey , to => $tokey , label => $label , color => $bgcolor , penwidth => $width , fillcolor => $bgcolor , fontcolor => $color );
270
+
271
+ $recursionmap -> {$key } = 1;
272
+
273
+ graph($key , $recursionmap , $graph );
274
+ }
275
+
276
+ if ($do_recurse ) {
277
+ foreach my $link (@{$epic_issues -> {issues }}) {
278
+ my $status = $link -> {fields }-> {status }-> {statusCategory }-> {key };
279
+
280
+ $color = status_color($status );
281
+ my $bgcolor = status_bgcolor($status );
282
+ my $width = status_width($status );
283
+ my $label = status_label($status , ' Epic Link' );
284
+
285
+ $graph -> add_node(name => $link -> {key }, color => $bgcolor , fillcolor => $bgcolor );
286
+ $graph -> add_edge(from => $iss , to => $link -> {key }, label => $label , fontcolor => ' black' , penwidth => $width , fillcolor => $bgcolor , color => $bgcolor );
287
+ graph($link -> {key }, $recursionmap , $graph );
288
+ }
289
+ }
290
+
291
+ eval { $graph -> run(format => ' svg' , output_file => " ./$iss .svg" ) };
292
+ }
293
+
164
294
sub help {
165
295
my $topic = shift ;
166
296
@@ -177,6 +307,13 @@ Commands:
177
307
c
178
308
create Create a new issue in the current project
179
309
310
+ e
311
+ epic Jump to epic for the current issue
312
+
313
+ g
314
+ graph [key] Creates a dependency graph, outputs to key.svg
315
+ Uses current issue if no key is provided.
316
+
180
317
h
181
318
help This help text
182
319
@@ -329,6 +466,16 @@ sub search {
329
466
return $ret ;
330
467
}
331
468
469
+ sub is_epic {
470
+ my $obj = shift ;
471
+
472
+ if (defined $obj -> {fields }-> {$customfields_n2i -> {$cfg -> param(' epicname' )}}) {
473
+ return 1;
474
+ }
475
+
476
+ return 0;
477
+ }
478
+
332
479
sub show {
333
480
my $key = shift ;
334
481
@@ -339,11 +486,28 @@ sub show {
339
486
};
340
487
return " Error with getting issue '$key ': $@ " unless ($res );
341
488
489
+ my $isstype = " Issue" ;
490
+ my $epic_text = " Epic: " ;
491
+
492
+ if (is_epic($res )) {
493
+ $isstype = " Epic" ;
494
+ $isstype = " Epic" ;
495
+ $epic_text .= ' This issue is an epic' ;
496
+ } else {
497
+ $epic_text .= defined ($res -> {fields }-> {$customfields_n2i -> {$cfg -> param(' epiclink' )}})
498
+ ? $res -> {fields }-> {$customfields_n2i -> {$cfg -> param(' epiclink' )}}
499
+ : ' This issue is not part of an epic' ;
500
+ }
501
+
342
502
$curissue = $key ;
503
+
504
+ $prompt = " $curproj show $curissue J> " ;
505
+
343
506
return <<EOF
344
- Issue [$key ]:
507
+ $isstype [$key ]:
345
508
Summary: $res ->{fields}->{summary}
346
509
Priority: $res ->{fields}->{priority}->{name}
510
+ $epic_text
347
511
Description:
348
512
$res ->{fields}->{description}
349
513
EOF
@@ -359,6 +523,12 @@ my $main_cmds = {
359
523
' c' => \&create,
360
524
' create' => \&create,
361
525
526
+ ' e' => \&epic,
527
+ ' epic' => \&epic,
528
+
529
+ ' g' => \&graph,
530
+ ' graph' => \&graph,
531
+
362
532
' h' => \&help,
363
533
' help' => \&help,
364
534
0 commit comments