jira_reporting 0.0.1

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.
data/bin/maint_vs_enh ADDED
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift "#{File.dirname(__FILE__)}/../lib"
4
+ require 'jira_reporting'
5
+ require 'pry-byebug'
6
+ require 'active_support'
7
+ require 'yaml'
8
+ require 'code_climate'
9
+ require 'auto_hash'
10
+ require 'pull_request'
11
+ require 'yaml/dbm'
12
+
13
+ auth = YAML.load_file('auth.yml')
14
+
15
+ jira_username = auth["jira"]["username"]
16
+ jira_password = auth["jira"]["password"]
17
+ JiraReporting.connect! jira_username, jira_password
18
+
19
+ start_date = (ARGV[0] && Date.parse(ARGV[0])) || 30.days.ago
20
+ end_date = (ARGV[1] && Date.parse(ARGV[1])) || 1.day.ago
21
+
22
+ start_date = start_date.strftime("%Y-%m-%d")
23
+ end_date = end_date.strftime("%Y-%m-%d")
24
+
25
+ puts "Start: #{start_date} END: #{end_date}"
26
+
27
+ def db
28
+ @db ||= YAML::DBM.open('maint_vs_enh_cache', DBM::WRCREAT)
29
+ end
30
+
31
+ def cache_issues(start_date, end_date)
32
+
33
+ # This takes a while
34
+ # query = %Q{
35
+ # project in (BULQ, DT, BLINQ.com, OptiTurn, "Tech Support", Tech)
36
+ # and status was "In Progress" DURING ("#{start_date}", "#{end_date}")
37
+ # }
38
+ query = %Q{
39
+ status was "In Progress" DURING ("#{start_date}", "#{end_date}")
40
+ }
41
+
42
+ puts "Searching jira: #{query}"
43
+ issues = JiraIssue.find(query)
44
+ puts "Search complete."
45
+
46
+ issue_count = issues.count
47
+ issues.each_with_index do |issue, index|
48
+ print "#{index + 1}/#{issue_count} (#{(((index+1).to_r/issue_count)*100).to_i }%) #{issue.key} "
49
+ if !db[issue.key]
50
+ issue.changelog
51
+ db[issue.key] = issue
52
+ puts "found"
53
+ else
54
+ puts "cached"
55
+ end
56
+ end
57
+ end
58
+
59
+ # cache_issues(start_date, end_date)
60
+
61
+ # binding.pry
62
+ # puts "yep"
63
+
64
+ # status_ages = {}
65
+ # if !db[issue.key] || !db[issue.key][:in_progress_duration]
66
+ # in_progress_duration = issue.status_duration_during("In Progress", start_date, end_date)
67
+ # db[issue.key] = {
68
+ # type: issue.type,
69
+ # in_progress_duration: in_progress_duration,
70
+ # issue: issue
71
+ # }
72
+ # end
73
+ # status_ages = db[issue.key][:status_ages]
74
+
75
+ # puts "#{issue.key} #{issue.type} (#{index + 1}/#{issue_count})"
76
+ # type_times[issue.type] += (status_ages["In Progress"] || 0)
77
+ # type_counts[issue.type] += 1
78
+ # puts type_times
79
+ # puts type_counts
80
+
81
+ # type_counts.keys.each { |type|
82
+ # puts [type, type_counts[type], type_times[type]].join(",")
83
+ # }
84
+
85
+ # binding.pry
86
+
87
+ puts "Loading issues"
88
+ issues = db.values
89
+ count = issues.count
90
+
91
+ puts "Dumping issues"
92
+ File.open("result.csv", "w") do |csv|
93
+ csv.puts("project,key,type,assignee,mover,in_progress_hours,summary,epic_key,epic_name,start,end")
94
+ issues.each_with_index do |issue, index|
95
+ puts "Processing... #{index+1}/#{count}"
96
+
97
+ epic_key = issue.orig["fields"]["customfield_10008"]
98
+ if epic_key
99
+ epic_issue = db[epic_key] || JiraIssue.find("issue = #{epic_key}").first
100
+ db[epic_key] ||= epic_issue
101
+ epic_name = epic_issue.orig["fields"]["customfield_10009"] if epic_issue
102
+ end
103
+
104
+ status_ages = issue.status_ages
105
+ out_of_in_progress_by = issue.status_changes.select{|c| c[:status] == "In Progress"}.last.andand[:author_out]
106
+ summary = issue.summary
107
+ summary.gsub!(',','')
108
+ epic_name ||= ""
109
+ epic_name.gsub!(',','')
110
+ row = [
111
+ issue.project,
112
+ issue.key,
113
+ issue.type,
114
+ (issue.orig["fields"]["assignee"].andand["name"] || ""),
115
+ out_of_in_progress_by || "",
116
+ status_ages["In Progress"] || 0,
117
+ summary,
118
+ epic_key || "",
119
+ epic_name || "",
120
+ issue.created_at,
121
+ issue.status_changes.last[:start]
122
+ ].join(",")
123
+ puts row
124
+ csv.puts(row)
125
+ end
126
+ end
127
+
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env ruby
2
+ PROJECT_ROOT = "#{File.dirname(__FILE__)}/../"
3
+
4
+ $:.unshift "#{File.dirname(__FILE__)}/../lib"
5
+ require 'jira_reporting'
6
+ require 'active_support'
7
+ require 'yaml'
8
+ require 'code_climate'
9
+ require 'auto_hash'
10
+ require 'pull_request'
11
+ require 'os'
12
+
13
+ report = AutoHash.new
14
+
15
+ auth = YAML.load_file(File.join(PROJECT_ROOT,'auth.yml'))
16
+ jira_username = auth["jira"]["username"]
17
+ jira_password = auth["jira"]["password"]
18
+ JiraReporting.connect! jira_username, jira_password
19
+
20
+ # If run on a day to calculate stats for the previous week, do not have to enter a start or end date.
21
+ start_date = (ARGV[0] && Date.parse(ARGV[0])) || 7.day.ago
22
+ end_date = (ARGV[1] && Date.parse(ARGV[1])) || 0.day.ago
23
+
24
+ start_date = start_date.strftime("%Y-%m-%d")
25
+ end_date = end_date.strftime("%Y-%m-%d")
26
+
27
+ puts ''
28
+ puts "Report dates - START: #{start_date} END: #{end_date}"
29
+ puts ''
30
+
31
+ puts 'Beginning report data pull...'
32
+ puts ''
33
+
34
+ # Closed issues during given date range
35
+ closed_ticket_query = %Q{
36
+ project = "Tech Support"
37
+ AND "SLA: Closed at" >= "#{start_date}"
38
+ AND "SLA: Closed at" < "#{end_date}"
39
+ AND status != "Not Accepted"
40
+ AND status != "Can't Reproduce"
41
+ AND type != Epic
42
+ }
43
+ puts "The following query will be run for closed tickets: #{closed_ticket_query}"
44
+
45
+ # Created issues during given date range
46
+ created_ticket_query = %Q{
47
+ project = "Tech Support"
48
+ AND created >= "#{start_date}"
49
+ AND created < "#{end_date}"
50
+ AND status != "Not Accepted"
51
+ AND status != "Can't Reproduce"
52
+ AND type != Epic
53
+ }
54
+ puts ''
55
+ puts "The following query will be run for created tickets: #{created_ticket_query}"
56
+
57
+ ISSUE_SEGMENTS = %i{bug operational feature research affects_warehouse_users affects_customers affects_client_corporate_users affects_internal_optoro_users}
58
+
59
+ # This method creates a hash object of issue arrays divided segments as keys.
60
+ def segment_issues(issues, segments)
61
+ segmented_issues = segments.inject({}){|segments_hash, segment| segments_hash[segment] = []; segments_hash }
62
+ issues.each do |issue|
63
+ segments.each do |segment|
64
+ segmented_issues[segment] << issue if issue.send("#{segment}?")
65
+ end
66
+ end
67
+ segmented_issues
68
+ end
69
+
70
+ created_ts_issues = JiraIssue.find(created_ticket_query)
71
+ total_created_ts_issues = created_ts_issues.count
72
+ created_segmented_issues = segment_issues(created_ts_issues, ISSUE_SEGMENTS)
73
+ created_ts_bug_count = created_segmented_issues[:bug].count
74
+ created_ts_ops_count = created_segmented_issues[:operational].count
75
+ created_ts_feature_requests = created_segmented_issues[:feature].count
76
+ created_ts_research_requests = created_segmented_issues[:research].count
77
+ created_ts_affecting_wh_users = created_segmented_issues[:affects_warehouse_users].count
78
+ created_ts_affecting_customers = created_segmented_issues[:affects_customers].count
79
+ created_ts_affecting_client_corporate_users = created_segmented_issues[:affects_client_corporate_users].count
80
+ created_ts_affecting_internal_users = created_segmented_issues[:affects_internal_optoro_users].count
81
+ created_ts_over_triage_sla = created_ts_issues.select { |issue| issue.over_triage_sla? == true }.count
82
+ created_ts_triaged_by_ed = created_ts_issues.select { |issue| issue.triager == 'ewright' }.count
83
+ created_ts_triaged_by_gabriel = created_ts_issues.select { |issue| issue.triager == 'gzurita' }.count
84
+ created_ts_triaged_by_spencer = created_ts_issues.select { |issue| issue.triager == 'sgilbert' }.count
85
+ created_ts_triaged_by_jason = created_ts_issues.select { |issue| issue.triager == 'jfogg' }.count
86
+
87
+ closed_ts_issues = JiraIssue.find(closed_ticket_query)
88
+
89
+ sla_counted_issues = closed_ts_issues.reject { |issue| issue.sla_due_time.nil? }
90
+
91
+ total_ts_cumulative_delay = sla_counted_issues.inject(0){|sum, issue| sum + issue.sum_total_time_over_sla }
92
+ total_closed_ts_issues = closed_ts_issues.count
93
+ total_sla_counted_issues = sla_counted_issues.count
94
+ total_on_time_issues = sla_counted_issues.select { |issue| issue.over_sla.nil? || issue.over_sla? == false }.count
95
+ closed_segmented_issues = segment_issues(closed_ts_issues, ISSUE_SEGMENTS)
96
+ closed_ts_bug_count = closed_segmented_issues[:bug].count
97
+ closed_ts_ops_count = closed_segmented_issues[:operational].count
98
+ closed_ts_feature_requests = closed_segmented_issues[:feature].count
99
+ closed_ts_research_requests = closed_segmented_issues[:research].count
100
+
101
+ tickets_created_to_tickets_closed = ( total_created_ts_issues.to_f / total_closed_ts_issues.to_f * 100 ).round(2)
102
+
103
+ report[:week] = {
104
+ closed_ts_cumulative_delay: total_ts_cumulative_delay,
105
+ closed_ts_count: total_closed_ts_issues,
106
+ closed_bug_count: closed_ts_bug_count,
107
+ closed_ops_count: closed_ts_ops_count,
108
+ closed_feature_requests: closed_ts_feature_requests,
109
+ closed_research_requests: closed_ts_research_requests,
110
+ created_ts_count: total_created_ts_issues,
111
+ created_bug_count: created_ts_bug_count,
112
+ created_ops_count: created_ts_ops_count,
113
+ created_feature_requests: created_ts_feature_requests,
114
+ created_research_requests: created_ts_research_requests,
115
+ created_issues_affecting_wh_users: created_ts_affecting_wh_users,
116
+ created_issues_affecting_customers: created_ts_affecting_customers,
117
+ created_issues_affecting_client_corporate_users: created_ts_affecting_client_corporate_users,
118
+ created_issues_affecting_internal_users: created_ts_affecting_internal_users,
119
+ tickets_created_to_closed_ratio: tickets_created_to_tickets_closed,
120
+ created_issues_over_triage_sla: created_ts_over_triage_sla,
121
+ created_issues_triaged_by_ed: created_ts_triaged_by_ed,
122
+ created_issues_triaged_by_gabriel: created_ts_triaged_by_gabriel,
123
+ created_issues_triaged_by_spencer: created_ts_triaged_by_spencer,
124
+ created_issues_triaged_by_jason: created_ts_triaged_by_jason
125
+ }
126
+
127
+ puts ''
128
+
129
+ priorities = closed_ts_issues.group_by(&:priority)
130
+
131
+ [:p1,:p2,:p3,:p4,:p5].each do |priority|
132
+ closed = priorities[priority] || []
133
+ trend_hit = 1.0
134
+ trend_hit = closed.select{|issue| issue.over_sla.nil? || issue.over_sla? == false }.length / closed.length.to_f if closed.any?
135
+
136
+ report[:sla][priority] = {
137
+ avg_hit_rate: (trend_hit * 100).round(2),
138
+ }
139
+
140
+ puts "Average hit rate for #{priority.to_s.upcase} tickets: #{(trend_hit * 100).round(2)}%"
141
+ puts ''
142
+ end
143
+
144
+ puts ''
145
+ puts 'The following issues went over SLA during the queried period:'
146
+ closed_ts_issues.sort_by(&:priority).each do |issue|
147
+ if issue.over_sla?
148
+ puts "#{issue.priority}\t#{issue.key}\t#{issue.assigned_team}"
149
+ end
150
+ end
151
+
152
+ puts ''
153
+ puts "These issues did not meet Platform Stability's triage goals:"
154
+ closed_ts_issues.sort_by(&:priority).each do |issue|
155
+ if issue.over_triage_sla?
156
+ puts "#{issue.priority}\t#{issue.key}\t#{issue.triager}"
157
+ end
158
+ end
159
+
160
+ overall_hit = total_on_time_issues.to_f / total_sla_counted_issues.to_f
161
+ report[:sla][:overall][:avg_hit_rate] = (overall_hit * 100).round(2)
162
+ puts ''
163
+ puts "Overall Avg Hit Rate: #{(overall_hit * 100).round(2)}%"
164
+
165
+ def copy_paste_string(report)
166
+ [
167
+ report[:week][:closed_ts_cumulative_delay].to_s,
168
+ report[:sla][:overall][:avg_hit_rate].to_s + '%',
169
+ report[:sla][:p1][:avg_hit_rate].to_s + '%',
170
+ report[:sla][:p2][:avg_hit_rate].to_s + '%',
171
+ report[:sla][:p3][:avg_hit_rate].to_s + '%',
172
+ report[:sla][:p4][:avg_hit_rate].to_s + '%',
173
+ report[:week][:closed_ts_count],
174
+ report[:week][:closed_bug_count].to_s,
175
+ report[:week][:closed_ops_count].to_s,
176
+ report[:week][:closed_feature_requests].to_s,
177
+ report[:week][:closed_research_requests].to_s,
178
+ report[:week][" "].to_s,
179
+ report[:week][:created_ts_count].to_s,
180
+ report[:week][:created_bug_count].to_s,
181
+ report[:week][:created_ops_count].to_s,
182
+ report[:week][:created_feature_requests].to_s,
183
+ report[:week][:created_research_requests].to_s,
184
+ report[:week][:created_issues_affecting_wh_users].to_s,
185
+ report[:week][:created_issues_affecting_customers].to_s,
186
+ report[:week][:created_issues_affecting_client_corporate_users].to_s,
187
+ report[:week][:created_issues_affecting_internal_users].to_s,
188
+ report[:week][" "].to_s,
189
+ report[:week][:tickets_created_to_closed_ratio].to_s + '%',
190
+ report[:week][" "].to_s,
191
+ report[:week][:created_issues_over_triage_sla].to_s,
192
+ report[:week][:created_issues_triaged_by_ed].to_s,
193
+ report[:week][:created_issues_triaged_by_gabriel].to_s,
194
+ report[:week][:created_issues_triaged_by_spencer].to_s,
195
+ report[:week][:created_issues_triaged_by_jason].to_s
196
+ ].join("\n")
197
+ end
198
+
199
+ # Copy/pasteable
200
+ puts "Copy/paste:\n----------"
201
+ puts copy_paste_string(report)
202
+ puts "-----------"
203
+
204
+ case
205
+ when OS.linux? == true
206
+ IO.popen("xclip -selection clipboard", "r+") do |xclip|
207
+ xclip.puts copy_paste_string(report)
208
+ end
209
+ when OS.mac? == true
210
+ IO.popen("pbcopy", "r+") do |pbcopy|
211
+ pbcopy.puts copy_paste_string(report)
212
+ end
213
+ else
214
+ puts "The automatic copying of the kpi report stats failed. Please manually copy and paste the output into the KPI spreadsheet"
215
+ end
data/bin/pr-report.rb ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift "#{File.dirname(__FILE__)}/../lib"
4
+ require 'pull_request'
5
+ require 'pry-byebug'
6
+
7
+ auth = YAML.load_file('auth.yml')
8
+ jira_username = auth["jira"]["username"]
9
+ jira_password = auth["jira"]["password"]
10
+ JiraReporting.connect! jira_username, jira_password
11
+
12
+ github_token = auth["github"]["api_token"]
13
+ PullRequest.connect(github_token)
14
+
15
+ prs = PullRequest.get_prs('inventory')
16
+ binding.pry
17
+
18
+ keys = [:pr_number, :jira, :title, :created, :requester, :cl, :ci, :code_reviewed, :needs_testing, :project, :issue_current_sprint, :issue_status]
19
+ puts keys.to_csv
20
+ prs.each do |pr|
21
+ puts pr.to_csv
22
+ end
23
+
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+ require 'jira_reporting'
3
+ require 'pry'
4
+ require 'byebug'
5
+
6
+ username = "hi"
7
+ password = "there"
8
+ start_date = "2014-07-01"
9
+ end_date = "2014-10-01"
10
+
11
+ JiraReporting.connect! username, password
12
+
13
+ r = JiraReporting::SLAReport.new
14
+ query = %Q{project = "Tech Support" AND status not in ("Not Accepted", "Can't Reproduce") AND "Support xt Resolved" >= #{start_date} AND "Support xt Resolved" < #{end_date}}
15
+ issues = r.find_all(query)
16
+ rept = issues.map{|i| JiraIssue.new(i) }
17
+ prios = rept.group_by(&:priority)
18
+
19
+
20
+ hour = 60 * 60.0
21
+ ideal_times = {
22
+ p1: 2 * hour,
23
+ p2: 24 * hour,
24
+ p3: (24 * hour) * 3, # 5 business days
25
+ p4: (24 * hour) * 5 # 5 business days
26
+ }
27
+
28
+ [:p1,:p2,:p3,:p4,:p5].each do |priority|
29
+ set = prios[priority] || []
30
+ open, closed = set.partition(&:open?)
31
+ trend_time = closed.inject(0){|s,t| s + t.sla_diff } / closed.length.to_f
32
+
33
+
34
+ trend_resolution_time = ""
35
+ if closed.any?
36
+ mm, ss = trend_time.divmod(60)
37
+ hh, mm = mm.divmod(60)
38
+ dd, hh = hh.divmod(24)
39
+ trend_resolution_time = "#{dd}d #{hh.to_i}h #{mm.to_i}m #{ss.to_i}s"
40
+ end
41
+
42
+ unless priority == :p4
43
+ ideal_time = ideal_times[priority]
44
+ multiple_off_ideal = trend_time.to_f / ideal_time
45
+ end
46
+
47
+ trend_hit = 0
48
+ trend_hit = closed.select{|t| t.sla_diff <= 0 }.length / closed.length.to_f if closed.any?
49
+
50
+ puts "#{priority.to_s.upcase}:"
51
+ puts "Open Tickets: #{open.count}"
52
+ puts "Closed Tickets: #{closed.count}"
53
+ puts "Avg Diff to SLA: #{trend_resolution_time}"
54
+ puts "Avg SLA Multiple: #{multiple_off_ideal.round(2)}x" unless priority == :p4
55
+ puts "Avg Hit Rate: #{(trend_hit * 100).round(2)}%"
56
+ puts ""
57
+ end
58
+
59
+ overall_hit = rept.select{|t| t.sla_diff <= 0 }.length / rept.length.to_f
60
+ puts ""
61
+ puts "Overall Avg Hit Rate: #{(overall_hit * 100).round(2)}%"
62
+
63
+ binding.pry
64
+
65
+ puts 2 + 2
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ PROJECT_ROOT = "#{File.dirname(__FILE__)}/../"
4
+
5
+ $:.unshift "#{File.dirname(__FILE__)}/../lib"
6
+ require 'jira_reporting'
7
+ require 'pry-byebug'
8
+ require 'active_support'
9
+ require 'yaml'
10
+
11
+ auth = YAML.load_file(File.join(PROJECT_ROOT,'auth.yml'))
12
+ jira_username = auth["jira"]["username"]
13
+ jira_password = auth["jira"]["password"]
14
+ JiraReporting.connect! jira_username, jira_password
15
+
16
+ # Open TS issues
17
+ query = %Q{
18
+ project = TS
19
+ AND ("Support xt Requester Review" >= startOfWeek(-1)
20
+ OR "Support xt On Production" >= startOfWeek(-1)
21
+ OR "Support xt Merged" >=startOfWeek(-1) )
22
+ AND status != "Not Accepted"
23
+ AND type != Epic
24
+ }
25
+
26
+ ts_issues = JiraIssue.find(query).sort_by(&:sla_time_ratio)
27
+
28
+ ts_issues.each do |issue|
29
+ issue.set_sla_due_time! if issue.sla_due_time.nil?
30
+ issue.set_sla_triaged_at! if issue.sla_triaged_at.nil?
31
+ issue.set_over_triage_sla! if issue.over_triage_sla.nil?
32
+ issue.set_sla_closed_at! if issue.sla_closed_at.nil?
33
+ issue.set_sla_due_warning! if issue.sla_due_warning.nil?
34
+ issue.set_over_sla! if issue.over_sla.nil?
35
+ puts "#{issue.priority} #{issue.key} #{issue.summary} has been updated."
36
+ end
37
+
38
+ puts 'SLA due time and overdue status update complete.'