jira_reporting 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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.'