Newer
Older
cramakri
committed
#!/usr/bin/env ruby
require 'rubygems'
require 'json'
require 'pp'
cramakri
committed
#
# = A wrapper for scripting jira
#
# Try the following commands:
#
# jira sprint S133 [lists the tasks in S133]
# jira plan S134 [shows the tasks planned for S134 in a form suitable for planning]
#
cramakri
committed
# Uses the Rest API: http://docs.atlassian.com/jira/REST/4.2.1/
#
# ----
#
# This script requires the json gem:
#
# http://flori.github.com/json/
#
# Which can be installed:
#
# gem install json
#
#
# == Preferences
#
# The url for JIRA
$jira_url = 'https://jira-bsse.ethz.ch'
# The url portion for the API
#$jira_api_url = "#{$jira_url}/rest/api/2.0.alpha1"
$jira_api_url = "#{$jira_url}/rest/api/2"
cramakri
committed
# Prefs path
$jira_prefs_path = File.expand_path('~/.jira')
# Cookie location
$jira_cookie_path = File.join($jira_prefs_path, 'cookie.txt')
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#
# == Issue Object
#
class Issue
def initialize(data)
@issue = data
end
def key
return @issue["key"]
end
def fields
return @issue["fields"]
end
def implements
implements = nil
links = self.fields["issuelinks"]
unless links.nil?
links.each { | link | implements = Issue.new(link["outwardIssue"]) if "implements" == link["type"]["outward"] && !link["outwardIssue"].nil? }
end
return implements
end
def implemented_by
implementedby = []
links = self.fields["issuelinks"]
unless links.nil?
links.each { | link | implementedby << Issue.new(link["inwardIssue"]) if "implements" == link["type"]["outward"] && !link["inwardIssue"].nil?}
end
return implementedby
end
def time
return 0 if self.fields["timetracking"].nil?
return self.fields["timetracking"]["remainingEstimateSeconds"] ? self.fields["timetracking"]["remainingEstimateSeconds"] : 0
end
def status
return self.fields["status"] ? self.fields["status"]["name"] : nil
end
def tester
return self.fields["customfield_10250"] ? self.fields["customfield_10250"]["name"] : nil
end
def summary
return self.fields["summary"]
end
def fix_version
fix_versions = self.fields["fixVersions"]
return "Unscheduled" if fix_versions.nil?
return fix_versions[0]["name"]
end
def transitions
@issue["transitions"]
end
def next_sprint
self.fields["customfield_10550"]
end
def resolved_or_closed?
status = self.status
(status == "Resolved" || status == "Closed")
end
end
cramakri
committed
#
# A module that implements some helpful operations
#
module JiraHelpers
def JiraHelpers.search(query, limit=nil)
search_cmd = "curl -s --get --cookie #{$jira_cookie_path} '#{$jira_api_url}/search' --data-urlencode 'os_authType=cookie' --data-urlencode 'jql=#{query}' --data-urlencode 'fields=*all,-comment'"
search_cmd = search_cmd + " --data-urlencode 'maxResults=#{limit}'" unless limit.nil?
return `#{search_cmd}`
cramakri
committed
end
def JiraHelpers.issue(issue_number, limit=nil)
issue_cmd = "curl -s --get --cookie #{$jira_cookie_path} '#{$jira_api_url}/issue/#{issue_number}' --data-urlencode 'os_authType=cookie' --data-urlencode 'expand=transitions'"
data = `#{issue_cmd}`
issue_data = JSON.load(data)
return Issue.new(issue_data)
end
def JiraHelpers.rank(issue, after, before=nil)
rank_data = {"issueKeys" => [issue], "customFieldId" => 10050 }
rank_data["rankAfterKey"] = after
rank_data["rankBeforeKey"] = before unless before.nil?
rank_cmd = "curl -s --cookie #{$jira_cookie_path} -H 'Content-Type: application/json' -X PUT '#{$jira_url}/rest/greenhopper/1.0/rank' -d '#{JSON.generate(rank_data)}'"
return `#{rank_cmd}`
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
end
def JiraHelpers.create_sp_issue(summary, description, fixversion)
# May need to escape the summary
issue_data = {
"fields" => {
"project" => {"key" => "SP" } ,
"summary" => summary,
"description" => description,
"fixVersions" => [ {"name" => fixversion } ],
"issuetype" => {"name" => "Task"}
}
}
issue_cmd = "curl -s --cookie #{$jira_cookie_path} -H 'Content-Type: application/json' '#{$jira_api_url}/issue/' -d '#{JSON.generate(issue_data)}'"
ans = `#{issue_cmd}`
return JSON.load(ans)
end
def JiraHelpers.link_sp_to_bis(sp_key, bis_key)
link_data = {
"type" => { "name" => "Hierarchy" },
"inwardIssue" => { "key" => sp_key },
"outwardIssue" => { "key" => bis_key }
}
link_cmd = "curl -s --cookie #{$jira_cookie_path} -H 'Content-Type: application/json' '#{$jira_api_url}/issueLink' -d '#{JSON.generate(link_data)}'"
return `#{link_cmd}`
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
end
def JiraHelpers.remove_next_sprint(bis_key)
update_data = {
"fields" => {
"customfield_10550" => nil
}
}
update_cmd = "curl -s --cookie #{$jira_cookie_path} -H 'Content-Type: application/json' '#{$jira_api_url}/issue/#{bis_key}' -X PUT -d '#{JSON.generate(update_data)}'"
return `#{update_cmd}`
end
def JiraHelpers.set_next_sprint(bis_key)
update_data = {
"fields" => {
"customfield_10550" => [
{"value"=>"Yes", "id"=>"10240", "self"=>"https://jira-bsse.ethz.ch/rest/api/2/customFieldOption/10240"}
]
}
}
update_cmd = "curl -s --cookie #{$jira_cookie_path} -H 'Content-Type: application/json' '#{$jira_api_url}/issue/#{bis_key}' -X PUT -d '#{JSON.generate(update_data)}'"
return `#{update_cmd}`
end
def JiraHelpers.set_fix_version(bis_key, fixversion)
update_data = { "fields" => { "fixVersions" => [ {"name" => fixversion } ] } }
update_cmd = "curl -s --cookie #{$jira_cookie_path} -H 'Content-Type: application/json' '#{$jira_api_url}/issue/#{bis_key}' -X PUT -d '#{JSON.generate(update_data)}'"
return `#{update_cmd}`
end
def JiraHelpers.transition(bis_key, trans_id)
transition_data = {
"transition" => { "id" => "#{trans_id}"}
}
transition_cmd = "curl -s --cookie #{$jira_cookie_path} -H 'Content-Type: application/json' '#{$jira_api_url}/issue/#{bis_key}/transitions' -d '#{JSON.generate(transition_data)}'"
return `#{transition_cmd}`
end
cramakri
committed
# Search and return the full data for the found objects
def JiraHelpers.search_full(query, silent)
print "Retrieving issues" unless silent
ans = JiraHelpers.search(query)
data = JSON.load(ans)
full_issues_data = data["issues"]
full_issues = full_issues_data.collect { | issue_data | Issue.new(issue_data) }
cramakri
committed
print "\n" unless silent
return full_issues
end
# Get the full data for entries that implement {issues}.
def JiraHelpers.retrieve_implementors(issues, silent)
print "Retrieving implementing issues" unless silent
implementors = []
issues.each do | issue |
print "." unless silent
implementors.concat issue.implemented_by
print "\n" unless silent
return implementors
end
def JiraHelpers.session_valid_raw
# Just get the response code, don't care about the rest
ans = `curl -s -w %{http_code} -o /dev/null --head --get --cookie #{$jira_cookie_path} --data-urlencode 'os_authType=cookie' '#{$jira_url}/rest/auth/1/session'`
return ans
cramakri
committed
def JiraHelpers.session_valid?
# Just get the response code, don't care about the rest
return false if $?.to_i != 0
return ans == "200"
cramakri
committed
end
end
#
# A module that implements some helpful operations
#
module InputHelpers
def InputHelpers.args(first_arg)
cmd = ARGV[first_arg .. -1].inject("") { | all, each | all + " " + each }
cmd.strip!
return cmd
end
def InputHelpers.sprint_name(sprint_name_or_number)
sprintNumber = "S" if sprint_name_or_number.nil?
sprintNumber = sprint_name_or_number.match("^[S|s].*") ? sprint_name_or_number : "S" + sprint_name_or_number
return sprintNumber
end
end
cramakri
committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
#
# == Commands
#
#
# The abstract superclass of commands
#
class JiraCommand
attr_accessor :silent
def initialize
@silent = true
end
# Return a description of the command to run
def description
return nil
end
# Run the command and return the result
def run
return nil
end
# Return true if the result should be printed.
#
# Default: print if the result is not empty.
def should_print_result(result)
return !result.empty?
end
# helper method to print time in a formatted way
def ptime(time)
time == 0 ? "" : "%.1fh" % (time / 3600.0)
end
cramakri
committed
end
#
# The help command
#
class Help < JiraCommand
def description
return "help"
end
def run
# show help
return <<-eos
NAME
jira
SYNOPSIS
jira commmand [arguments]
DESCRIPTION
A simple api for jira. Login as jira user and get some info. The available commands are:
login asks for login and password to jira and stores a session cookie
sprint S133 get the information about the given sprint
dump url print given url in pure json
plan 133 shows planned BIS issues
cust 10 shows an overview of the N highest-priority issues from customer projects (OBP, SOB)
search 'project=SP AND fixVersion = S139 ORDER BY \"Global Rank\"'
runs a jira query and shows the results
rank 133 ranks all issues in S133 in accordance with the ranks of the BIS issues
rank SP-121 SP-122
ranks SP-121 after SP-122
kanban 133 returns a Kanban list of the issues in S133
create 133 create issues for all BIS issues designated as next sprint
create 133 BIS-111
create an issue in S133 for BIS-111
start 133 mark all BIS issues in S133 as "Active in sprint" and clear the "Next Sprint" flag
finish 133 Show whether the parent issues to those in S133 are resolved or not
finish 133 exec mark all BIS issues in S133 that have no other open issues as resolved.
Set "Next Sprint" for those with open issues.
move 133 Set the fix version to 133 for all issues that are connected to the BIS issues
move 133 SP-452 Set the fix version of SP-452 to 133
cramakri
committed
end
end
#
# The login command
#
class Login < JiraCommand
cramakri
committed
def initialize
super
print "Enter jira login (e.g. alincoln): "
@jira_user = $stdin.gets().strip
cramakri
committed
end
cramakri
committed
def description
return "login"
end
def run
# The url portion for logging in
# may also want to try curl -c cookie_jar -H "Content-Type: application/json" -d '{"username" : "admin", "password" : "admin"}' #{$jira_url}/jira/rest/auth/latest/session
cramakri
committed
jira_login = 'secure/Dashboard.jspa?os_authType=basic'
Dir.mkdir($jira_prefs_path) unless File.exists?($jira_prefs_path)
return `curl --head -s -u #{@jira_user} --cookie-jar #{$jira_cookie_path} '#{$jira_url}/#{jira_login}'`
cramakri
committed
end
end
#
# Check if the session is valid
#
class SessionValid < JiraCommand
def initialize
super
end
def description
return "session"
end
def run
return JiraHelpers.session_valid_raw
end
end
cramakri
committed
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
#
# Lists the issues in the sprint
#
class LoggedInCommand < JiraCommand
def run
Login.new.run unless File.exists?($jira_cookie_path)
Login.new.run unless JiraHelpers.session_valid?
return self.run_logged_in
end
# For subclasses to implement
def run_logged_in
return ""
end
end
#
# Shows the JSON for an issue -- useful for debugging
#
class DumpIssue < LoggedInCommand
def initialize
super
@issueUrl = ARGV[1]
end
def description
return "dump #{@issueUrl}"
end
def run_logged_in
if @issueUrl.nil?
ans = JiraHelpers.search("project=SP AND fixVersion = S133 ORDER BY \"Global Rank\" ASC")
@issueUrl = JSON.load(ans)["issues"][0]["self"]
end
ans = `curl -s --get --cookie #{$jira_cookie_path} '#{@issueUrl}' --data-urlencode 'expand=transitions'`
cramakri
committed
data = JSON.load(ans)
return JSON.pretty_generate(data)
end
end
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
#
# Prioritize an issue
#
# The REST API can be browsed using the REST endpoint browser. The particular endpoint you are looking for is a PUT request to /rest/greenhopper/1.0/rank. The request body looks # like:
# {"issueKeys":["ANERDS-102"],"rankBeforeKey":"ANERDS-94","rankAfterKey":"ANERDS-7","customFieldId":10050}
# The issueKeys are the items to rank (there can be several), the rankBeforeKey is the issue to rank the items before and the rankAfterKey is the issue to rank these items after. # Because there may be multuple Global Rank fields in your JIRA the customFieldId is used to specify which one is used for this ranking.
# "customfield_10050": {
# "required": false,
# "schema": {
# "type": "array",
# "items": "string",
# "custom": "com.pyxis.greenhopper.jira:gh-global-rank",
# "customId": 10050
# },
# "name": "Global Rank",
# "operations": ["set"]
# },
class RankIssue < LoggedInCommand
def initialize
super
@issue = ARGV[1]
@after = ARGV[2]
@before = ARGV[3]
end
def description
return "rank #{@issue} after #{@after} before #{@before}"
end
def run_logged_in
ans = JiraHelpers.rank(@issue, @after, @before)
return ans
end
end
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
class RankSprint < LoggedInCommand
def initialize
super
@sprintNumber = InputHelpers.sprint_name(ARGV[1])
end
def description
return "rank #{@sprintNumber}"
end
def run_logged_in_old
ans = JiraHelpers.rank(@issue, @after, @before)
return ans
end
def run_logged_in
sp_issues = retrieve_sprint_issues()
bis_issues = retrieve_bis_issues(sp_issues)
self.rank_issues(sp_issues, bis_issues)
self.print_issues
# Nothing to show
return "#{sp_issues.length} issues"
end
def retrieve_sprint_issues()
query = "project=SP AND fixVersion = #{@sprintNumber} ORDER BY \"Global Rank\" ASC"
sp_issues = JiraHelpers.search_full(query, @silent)
init_sp_issue_dict(sp_issues)
return sp_issues
end
def retrieve_bis_issues(sp_issues)
implemented_issues = []
sp_issues.each do | issue |
implements_key = issue.implements.key unless issue.implements.nil?
implemented_issues << issue.implements unless issue.implements.nil?
end
bis_issue_list = implemented_issues.collect{|issue| issue.key }.join(",")
bis_query= "project = BIS AND key in (#{bis_issue_list}) ORDER BY \"Global Rank\" ASC"
bis_issues = JiraHelpers.search_full(bis_query, @silent)
return bis_issues
end
def init_sp_issue_dict(sp_issues)
@sp_issue_dict = {}
sp_issues.each do | issue |
key = issue.key
@sp_issue_dict[key] = issue
end
end
def rank_issues(sp_issues, bis_issues)
not_in_bis = []
bis_ranking = []
seen_sp_issues = [].to_set
bis_issues.each do | bis_issue |
bis_issue.implemented_by.each do | sp |
unless @sp_issue_dict[sp.key].nil?
seen_sp_issues << sp.key
bis_ranking << @sp_issue_dict[sp.key]
end
end
end
sp_issues.each do | sp_issue |
not_in_bis << sp_issue unless seen_sp_issues.include?(sp_issue.key)
end
# not in bis comes first, in arbitrary order, then the remaining issues
ranked_issues = not_in_bis + bis_ranking
ranked_issues.each_cons(2) do | first, second |
JiraHelpers.rank(second.key, first.key)
end
end
def print_issues
query = "project=SP AND fixVersion = #{@sprintNumber} ORDER BY \"Global Rank\" ASC"
full_issues = JiraHelpers.search_full(query, @silent)
header = "%8s\t%12s\t%s" % ["Key", "Implements", "Summary"]
puts header
full_issues.each do | issue |
key = issue.key
implements_key = issue.implements.key unless issue.implements.nil?
summary = issue.summary
row = "%8s\t%12s\t%s" % [key, implements_key, summary]
puts row
end
print " ", ("-" * 27), "\n"
end
end
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
#
# Shows the JSON for a search results -- useful for debugging
#
class DumpSearch < LoggedInCommand
def initialize
super
@query = ARGV[1]
@count = ARGV[2]
end
def description
return "search #{@query} #{@count}"
end
def run_logged_in
@query = "project=SP AND fixVersion = S139 ORDER BY \"Global Rank\" ASC" if @query.nil?
@count = 1 if @count.nil?
ans = JiraHelpers.search(@query, @count)
data = JSON.load(ans)
return JSON.pretty_generate(data)
end
end
cramakri
committed
#
# Lists the issues in the sprint
#
class ListSprint < LoggedInCommand
def initialize
super
@sprintNumber = InputHelpers.sprint_name(ARGV[1])
cramakri
committed
end
def description
return "sprint #{@sprintNumber}"
end
def run_logged_in
query = "project=SP AND fixVersion = #{@sprintNumber} ORDER BY \"Global Rank\" ASC"
full_issues = JiraHelpers.search_full(query, @silent)
self.print_issues_table(full_issues)
# Nothing to show
return "#{full_issues.length} issues"
end
def print_issues_table(full_issues)
header = "%8s\t%12s\t%6s\t%12s\t%8s\t%s" % ["Key", "Implements", "Time", "Status", "Tester", "Summary"]
puts header
time_remaining = 0.0
full_issues.each do | issue |
key = issue.key
implements_key = issue.implements.key unless issue.implements.nil?
time = issue.time
status = issue.status
tester = issue.tester
summary = issue.summary
row = "%8s\t%12s\t%6s\t%12s\t%8s\t%s" % [key, implements_key, ptime(time), status, tester, summary]
cramakri
committed
puts row
# Tasks that are resolved can be considered to have 0 time remaining
time_remaining = time_remaining + time unless (status == "Resolved" || status == "Closed")
cramakri
committed
end
print " ", ("-" * 27), "\n"
puts " Time Remaining : %.1fh" % (time_remaining / 3600.0)
cramakri
committed
end
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
#
# Lists the issues in the kanban cycle
#
class ListKanban < LoggedInCommand
def initialize
super
@sprintNumber = InputHelpers.sprint_name(ARGV[1])
end
def description
return "kanban #{@sprintNumber}"
end
def run_logged_in
query = "project=BIS AND status not in (Resolved, Closed) OR (status = Resolved AND fixVersion = #{@sprintNumber}) ORDER BY \"Global Rank\" ASC"
full_issues = JiraHelpers.search_full(query, @silent)
self.print_issues_table(full_issues)
# Nothing to show
return "#{full_issues.length} issues"
end
def print_issues_table(full_issues)
header = "%8s %12s\t%12s\t%8s\t%s" % ["Key", "Implements", "Status", "Tester", "Summary"]
puts header
full_issues.each do | issue |
key = issue.key
implements_key = issue.implements.key unless issue.implements.nil?
status = issue.status
tester = issue.tester
summary = issue.summary
row = "%8s %12s\t%12s\t%8s\t%s" % [key, implements_key, status, tester, summary]
puts row
end
print " ", ("-" * 27), "\n"
end
end
cramakri
committed
#
# List the issues slated for a sprint in a form that is helpful for planning
#
class PlanSprint < LoggedInCommand
def initialize
super
@sprintNumber = InputHelpers.sprint_name(ARGV[1])
@total = 0
cramakri
committed
end
def description
return "plan #{@sprintNumber}"
end
def run_logged_in
sp_query = "project=SP AND fixVersion = #{@sprintNumber} ORDER BY \"Global Rank\" ASC"
sp_issues = JiraHelpers.search_full(sp_query, @silent)
init_sp_issue_dict(sp_issues)
bis_query= "project = BIS AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC"
bis_issues = JiraHelpers.search_full(bis_query, @silent)
enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(bis_issues, @silent))
cramakri
committed
ccs_query= "project = CCS AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC"
ccs_issues = JiraHelpers.search_full(ccs_query, @silent)
enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(ccs_issues, @silent))
swe_query= "project = SWE AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC"
swe_issues = JiraHelpers.search_full(swe_query, @silent)
enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(swe_issues, @silent))
print_issues_table("BIS", bis_issues)
print_issues_table("CCS", ccs_issues)
print_issues_table("SWE", swe_issues)
print_unseen_sp_issues_table(sp_issues)
puts ("=" * 12)
puts "Total %8.1fh" % [@total / 3600.0]
puts ("-" * 12)
cramakri
committed
# Nothing to show
issue_count = bis_issues.length + ccs_issues.length + swe_issues.length
return "#{issue_count} issues"
cramakri
committed
end
def init_sp_issue_dict(sp_issues)
@sp_issue_dict = {}
sp_issues.each do | issue |
key = issue.key
cramakri
committed
@sp_issue_dict[key] = issue
end
@seen_sp_issues = [].to_set
end
#
# Take those issues that are not yet resolved / closed and add them to the sp issues dict
#
def enrich_sp_issue_dict(implementors)
implementors.each do | issue |
sp = issue.key
next if @sp_issue_dict[sp]
status = issue.status
@sp_issue_dict[sp] = issue unless issue.resolved_or_closed?
end
end
def print_unseen_sp_issues_table(full_issues)
puts ("=" * 12)
puts "SP Missed"
puts ("-" * 12)
header = "%8s\t%12s\t%12s\t%6s\t%s" % ["Subtotal", "Key", "SP", "Time", "Summary"]
puts header
subtotal = 0.0
full_issues.each do | issue |
sp = issue.key
next if @seen_sp_issues.include?(sp)
key = "----"
key = issue.implements.key unless issue.implements.nil?
time = issue.time
summary = issue.summary
# Tasks that are resolved can be considered to have 0 time remaining
status = issue.status
subtotal = subtotal + time unless status == "Resolved"
row = "%8.1fh\t%12s\t%12s\t%6s\t%s" % [subtotal / 3600.0, key, sp, ptime(time), summary]
@total = @total + subtotal
cramakri
committed
end
def print_issues_table(title, full_issues)
puts ("=" * 12)
puts title
puts ("-" * 12)
header = "%8s\t%12s\t%12s\t%6s\t%s" % ["Subtotal", "Key", "SP", "Time", "Summary"]
puts header
subtotal = 0.0
full_issues.each do | issue |
key = issue.key
summary = issue.summary
parent = issue.fields["parent"]
parent = parent["key"] unless parent.nil?
summary = "#{parent} / #{summary}" unless parent.nil?
cramakri
committed
implementedby = []
issue.implemented_by.each do | sp_issue |
sp = sp_issue.key
cramakri
committed
# We are only interested in links to issues in the specified sprint
implementedby << sp if @sp_issue_dict[sp]
cramakri
committed
end
if implementedby.length < 1
row = "%8s\t%12s\t%12s\t%6s\t%s" % ["----", key, "----", "", summary]
cramakri
committed
puts row
next
end
implementedby.each_with_index do | sp, index |
# print one row for each implemented by
spissue = @sp_issue_dict[sp]
next if spissue.nil?
next if @seen_sp_issues.include?(sp)
spfields = spissue.fields
time = spissue.time
# Tasks that are resolved can be considered to have 0 time remaining
fix_version = spissue.fix_version
issue_in_different_sprint = fix_version != @sprintNumber
status = spissue.status
subtotal = subtotal + time unless (status == "Resolved" || status == "Closed" || issue_in_different_sprint)
cramakri
committed
if index < 1
issue_summary = summary
issue_summary = "[#{fix_version}] #{issue_summary}" if issue_in_different_sprint
row = "%8.1fh\t%12s\t%12s\t%6s\t%s" % [subtotal / 3600.0, key, sp, ptime(time), issue_summary]
cramakri
committed
else
issue_summary = "\""
issue_summary = "[#{fix_version}] #{issue_summary}" if issue_in_different_sprint
row = "%8.1fh\t%12s\t%12s\t%6s\t%s" % [subtotal / 3600.0, "\"", sp, ptime(time), issue_summary]
cramakri
committed
end
puts row
@seen_sp_issues.add(sp)
end
end
@total = @total + subtotal
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
#
# Create sprint issues for the issues in the sprint
#
class CreateSprint < LoggedInCommand
def initialize
super
@sprintNumber = ARGV[1]
@sprintNumber = "S" if @sprintNumber.nil?
@sprintNumber = "S" + @sprintNumber unless @sprintNumber.match("^[S|s].*")
@parent_issue = ARGV[2]
end
def description
return "create #{@sprintNumber}"
end
def run_logged_in
header = "%12s\t%12s\t%s" % ["Key", "SP", "Summary"]
puts header
# if no issue is specified, create all issues that relate to bis
return create_bis_issues if @parent_issue.nil?
# create the issue explicitly requested
return create_explicit_issue
end
def create_explicit_issue
issue = JiraHelpers.issue(@parent_issue)
create_issues(@parent_issue, [issue])
return "1 issue"
end
def create_bis_issues
sp_query = "project=SP AND fixVersion = #{@sprintNumber} ORDER BY \"Global Rank\" ASC"
sp_issues = JiraHelpers.search_full(sp_query, @silent)
init_sp_issue_dict(sp_issues)
bis_query= "project = BIS AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC"
bis_issues = JiraHelpers.search_full(bis_query, @silent)
enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(bis_issues, @silent))
ccs_query= "project = CCS AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC"
ccs_issues = JiraHelpers.search_full(ccs_query, @silent)
enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(ccs_issues, @silent))
swe_query= "project = SWE AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC"
swe_issues = JiraHelpers.search_full(swe_query, @silent)
enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(swe_issues, @silent))
create_necessary_issues("BIS", bis_issues)
# print_issues_table("CCS", ccs_issues)
# print_issues_table("SWE", swe_issues)
# Nothing to show
issue_count = bis_issues.length + ccs_issues.length + swe_issues.length
return "#{issue_count} issues"
end
def init_sp_issue_dict(sp_issues)
@sp_issue_dict = {}
sp_issues.each do | issue |
key = issue.key
@sp_issue_dict[key] = issue
end
@seen_sp_issues = [].to_set
end
#
# Take those issues that are not yet resolved / closed and add them to the sp issues dict
#
def enrich_sp_issue_dict(implementors)
implementors.each do | issue |
sp = issue.key
next if @sp_issue_dict[sp]
status = issue.status
@sp_issue_dict[sp] = issue unless (status == "Resolved" || status == "Closed")
end
end
def create_necessary_issues(title, full_issues)
issues_to_create = []
full_issues.each do | issue |
is_umbrella = !issue.fields["sub-tasks"].nil?
next if is_umbrella
implementedby = []
issue.implemented_by.each do | sp_issue |
sp = sp_issue.key
# We are only interested in links to issues in the specified sprint
implementedby << sp if @sp_issue_dict[sp]
end
issues_to_create << issue if implementedby.length < 1
end
create_issues(title, issues_to_create)
end
def create_issues(title, issues_to_create)
puts ("=" * 12)
puts title
puts ("-" * 12)
issues_to_create.each do | issue |
summary = "#{issue.key} : #{issue.summary}"
desc = "See #{issue.key}."
sp = JiraHelpers.create_sp_issue(summary, desc, @sprintNumber)
sp_key = sp["key"]
JiraHelpers.link_sp_to_bis(sp_key, issue.key)
row = "%12s\t%12s\t%s" % [issue.key, sp_key, summary]
puts row
end
end
end
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
#
# List the issues slated from customer projects
#
class CustIssues < LoggedInCommand
def initialize
super
end
def description
return "cust"
end
def run_logged_in
obp_query = "project=OBP AND status not in (Resolved, Closed) ORDER BY \"Global Rank\" ASC"
obp_issues = JiraHelpers.search_full(obp_query, @silent)
sob_query = "project=SOB AND status not in (Resolved, Closed) ORDER BY \"Global Rank\" ASC"
sob_issues = JiraHelpers.search_full(sob_query, @silent)
all_issues = [obp_issues, sob_issues].flatten
implementors = JiraHelpers.retrieve_implementors(all_issues, @silent)
init_implementors_dict(implementors)
print_issues_table("OBP", obp_issues)
print_issues_table("SOB", sob_issues)
# Nothing to show
return "#{all_issues.length} issues"
end
def init_implementors_dict(implementors)
@implementors_dict = {}
implementors.each do | issue |
@implementors_dict[key] = issue
end
end
def print_issues_table(title, full_issues)
puts ("=" * 12)
puts title
puts ("-" * 12)
header = "%12s\t%12s\t%s" % ["Key", "BIS", "Summary"]
puts header
full_issues.each do | issue |