zentool 0.8.0
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 +7 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +609 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +80 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +44 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/zentool +3 -0
- data/lib/zentool.rb +80 -0
- data/lib/zentool/article_helper.rb +102 -0
- data/lib/zentool/graph.rb +117 -0
- data/lib/zentool/metrics.rb +172 -0
- data/lib/zentool/ticket.rb +10 -0
- data/lib/zentool/version.rb +3 -0
- data/lib/zentool/zendesk_article.rb +84 -0
- data/lib/zentool/zendesk_ticket.rb +172 -0
- data/zentool.gemspec +35 -0
- metadata +111 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
# Graph class for pull_articles.rb
|
2
|
+
|
3
|
+
class Graph
|
4
|
+
def initialize(articles, sections, categories, basic_auth)
|
5
|
+
@articles, @sections, @categories, @basic_auth = articles, sections, categories, basic_auth
|
6
|
+
end
|
7
|
+
|
8
|
+
def generate
|
9
|
+
id_title_map = Graph.create_id_title_map(@articles)
|
10
|
+
@article_link_map = Graph.article_link_map(@articles, @categories, @sections, id_title_map, @basic_auth)
|
11
|
+
graph_settings
|
12
|
+
graph_nodes(id_title_map)
|
13
|
+
graph_edges(id_title_map)
|
14
|
+
@g.output(png: 'article_relationships.png')
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.wrap(s, width = 15)
|
18
|
+
s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create_id_title_map(articles)
|
22
|
+
x = {}
|
23
|
+
articles.each do |article|
|
24
|
+
x[article['id'].to_i] = article['title']
|
25
|
+
end
|
26
|
+
x
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.extract_links(string)
|
30
|
+
[URI.extract(string, /http(s)?/)].flatten
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.extract_IDs(string)
|
34
|
+
string.split(//).map { |x| x[/\d+/] }.compact.join('').to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.validate_link(article, link, basic_auth)
|
38
|
+
response = HTTParty.get(link, basic_auth)
|
39
|
+
unless response.code == 200
|
40
|
+
puts
|
41
|
+
puts 'Broken Link Found'
|
42
|
+
puts '-----------------'
|
43
|
+
puts
|
44
|
+
puts 'From page: ' + article['title']
|
45
|
+
puts 'At URL: ' + article['html_url']
|
46
|
+
puts
|
47
|
+
puts "Message: Error #{response.code}: #{response.message}"
|
48
|
+
puts 'Broken link: ' + link
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.article_link_map(articles, categories, sections, id_title_map, basic_auth)
|
55
|
+
puts 'Constructing article link map...'
|
56
|
+
article_link_map = {}
|
57
|
+
articles.each do |article|
|
58
|
+
unless (categories[sections[article['section_id']]['category_id']]['name'] == 'Announcements') || (article['body'].class != String)
|
59
|
+
referenced_links = Graph.extract_links(article['body'])
|
60
|
+
referenced_articles = []
|
61
|
+
unless referenced_links.empty?
|
62
|
+
referenced_links.each do |link|
|
63
|
+
if validate_link(article, link, basic_auth)
|
64
|
+
id = Graph.extract_IDs(link)
|
65
|
+
title = id_title_map[id]
|
66
|
+
unless (id.class == NilClass) || (title.class == NilClass) || (id.to_s.size != 9)
|
67
|
+
referenced_articles << Graph.wrap("#{title}\n#{id}")
|
68
|
+
end
|
69
|
+
else
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
article_link_map[article['id']] = referenced_articles
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
article_link_map
|
78
|
+
end
|
79
|
+
|
80
|
+
def graph_settings
|
81
|
+
$LOAD_PATH.unshift('../lib')
|
82
|
+
@g = GraphViz.new('G')
|
83
|
+
|
84
|
+
@g.node[:color] = '#222222'
|
85
|
+
@g.node[:style] = 'filled'
|
86
|
+
@g.node[:shape] = 'box'
|
87
|
+
@g.node[:penwidth] = '1'
|
88
|
+
@g.node[:fontname] = 'Helvetica'
|
89
|
+
@g.node[:fillcolor] = '#eeeeee'
|
90
|
+
@g.node[:fontcolor] = '#333333'
|
91
|
+
@g.node[:margin] = '0.05'
|
92
|
+
@g.node[:fontsize] = '12'
|
93
|
+
@g.edge[:color] = '#666666'
|
94
|
+
@g.edge[:weight] = '1'
|
95
|
+
@g.edge[:fontsize] = '10'
|
96
|
+
@g.edge[:fontcolor] = '#444444'
|
97
|
+
@g.edge[:fontname] = 'Helvetica'
|
98
|
+
@g.edge[:dir] = 'forward'
|
99
|
+
@g.edge[:arrowsize] = '1'
|
100
|
+
@g.edge[:arrowhead] = 'vee'
|
101
|
+
end
|
102
|
+
|
103
|
+
def graph_nodes(id_title_map)
|
104
|
+
nodes = []
|
105
|
+
@article_link_map.each do |id, _referenced_articles|
|
106
|
+
nodes << Graph.wrap("#{id_title_map[id]}\n#{id}")
|
107
|
+
end
|
108
|
+
@g.add_nodes(nodes)
|
109
|
+
end
|
110
|
+
|
111
|
+
def graph_edges(id_title_map)
|
112
|
+
@article_link_map.each do |id, referenced_articles|
|
113
|
+
@g.add_edges(Graph.wrap("#{id_title_map[id]}\n#{id}"), referenced_articles.map(&:to_s))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
# Represents an aggregate of metrics from multiple tickets
|
5
|
+
class Metrics
|
6
|
+
|
7
|
+
MINUTES_IN_DAY = 1440
|
8
|
+
MINUTES_IN_HOUR = 60
|
9
|
+
PLOT_WIDTH = 200
|
10
|
+
CURRENT_DATE = Date.today
|
11
|
+
attr_accessor :tickets, :avg_user_priority, :avg_development_priority,
|
12
|
+
:unsolved_tickets_by_age_log_scale, :solved_tickets_by_age_log_scale
|
13
|
+
|
14
|
+
def initialize(tickets)
|
15
|
+
@tickets = tickets
|
16
|
+
log_scale = {'0' => 0, '1' => 0, '2' => 0, '3' => 0, '4' => 0,
|
17
|
+
'5' => 0, '6-10' => 0, '11-20' => 0, '21-50' => 0, '51-100' => 0, '101+' => 0}
|
18
|
+
log_scale2 = log_scale.clone
|
19
|
+
@unsolved_tickets_by_age_log_scale = unsolved_age(log_scale)
|
20
|
+
@solved_tickets_by_age_log_scale = solved_age(log_scale2)
|
21
|
+
@avg_user_priority = avg_priority('user_priority')
|
22
|
+
@avg_development_priority = avg_priority('development_priority')
|
23
|
+
end
|
24
|
+
|
25
|
+
def plot_as_log(tickets_by_log_scale, age)
|
26
|
+
if age <= 5
|
27
|
+
tickets_by_log_scale[age.to_s] += 1
|
28
|
+
elsif age <= 10
|
29
|
+
tickets_by_log_scale['6-10'] += 1
|
30
|
+
elsif age <= 20
|
31
|
+
tickets_by_log_scale['11-20'] += 1
|
32
|
+
elsif age <= 50
|
33
|
+
tickets_by_log_scale['21-50'] += 1
|
34
|
+
elsif age <= 100
|
35
|
+
tickets_by_log_scale['51-100'] += 1
|
36
|
+
else
|
37
|
+
tickets_by_log_scale['101+'] += 1
|
38
|
+
end
|
39
|
+
tickets_by_log_scale
|
40
|
+
end
|
41
|
+
|
42
|
+
# Creates plot data for number of solved tickets by age
|
43
|
+
def unsolved_age(log_scale)
|
44
|
+
out_scale = Hash.new
|
45
|
+
@tickets.each do |ticket|
|
46
|
+
metrics = ticket.metrics
|
47
|
+
# Rounds the age of the ticket down to the nearest day
|
48
|
+
unless metrics['full_resolution_time_in_minutes']
|
49
|
+
if metrics['initially_assigned_at']
|
50
|
+
start_date = Date.parse metrics['initially_assigned_at']
|
51
|
+
age = (CURRENT_DATE - start_date).to_i
|
52
|
+
log_scale = plot_as_log(log_scale, age)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
log_scale
|
57
|
+
end
|
58
|
+
|
59
|
+
# Creates plot data for number of solved tickets by age
|
60
|
+
def solved_age(log_scale)
|
61
|
+
out_scale = Hash.new
|
62
|
+
@tickets.each do |ticket|
|
63
|
+
metrics = ticket.metrics
|
64
|
+
# Rounds the age of the ticket down to the nearest day
|
65
|
+
if metrics['full_resolution_time_in_minutes']
|
66
|
+
age = metrics['full_resolution_time_in_minutes'] / MINUTES_IN_DAY
|
67
|
+
log_scale = plot_as_log(log_scale, age)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
log_scale
|
71
|
+
end
|
72
|
+
|
73
|
+
# Creates plot data for average first reply time by priority
|
74
|
+
def avg_priority(priority_type)
|
75
|
+
# Hash with key => user_priority, and the value => array of reply times from tickets with that priority
|
76
|
+
tickets_by_priority = Hash.new {|h,k| h[k] = []}
|
77
|
+
|
78
|
+
# Hash with key => user_priority, and the value => average reply time of that priority
|
79
|
+
avg_priority = Hash.new(0)
|
80
|
+
|
81
|
+
tickets.each do |ticket|
|
82
|
+
|
83
|
+
priority = ticket.info[priority_type]
|
84
|
+
reply_time = ticket.metrics['reply_time_in_minutes']
|
85
|
+
|
86
|
+
if priority == nil
|
87
|
+
priority = 'None'
|
88
|
+
end
|
89
|
+
|
90
|
+
if reply_time
|
91
|
+
tickets_by_priority[priority] << reply_time
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
tickets_by_priority.each do |key, value|
|
96
|
+
if key == nil
|
97
|
+
key = 'None'
|
98
|
+
end
|
99
|
+
avg = value.inject(:+).to_f / value.length
|
100
|
+
avg_priority[key] = avg / MINUTES_IN_HOUR
|
101
|
+
end
|
102
|
+
avg_priority
|
103
|
+
end
|
104
|
+
|
105
|
+
# draws command line graph based on ticket metrics
|
106
|
+
def graph
|
107
|
+
puts
|
108
|
+
puts 'Age of Solved Tickets'
|
109
|
+
puts '_____________________'
|
110
|
+
puts 'Days Ticket-Count'
|
111
|
+
@solved_tickets_by_age_log_scale.keys.each do |age|
|
112
|
+
printf "%-10s %5d %s \n" % [age, @solved_tickets_by_age_log_scale[age],
|
113
|
+
'#' * (Math.log10(@solved_tickets_by_age_log_scale[age]+1)*5)]
|
114
|
+
end
|
115
|
+
|
116
|
+
puts
|
117
|
+
puts 'Age of Unsolved Tickets'
|
118
|
+
puts '_______________________'
|
119
|
+
puts 'Days Ticket-Count'
|
120
|
+
@unsolved_tickets_by_age_log_scale.keys.each do |age|
|
121
|
+
printf "%-10s %5d %s \n" % [age, @unsolved_tickets_by_age_log_scale[age],
|
122
|
+
'#' * (Math.log10(@unsolved_tickets_by_age_log_scale[age]+1)*5)]
|
123
|
+
end
|
124
|
+
|
125
|
+
puts
|
126
|
+
puts 'Average First Reply Time by Development Priority'
|
127
|
+
puts '___________________________________________'
|
128
|
+
puts 'Priority Reply-Time-Hours'
|
129
|
+
@avg_development_priority.keys.sort.each do |development_priority|
|
130
|
+
printf "%-10s %5d %s \n" % [development_priority, @avg_development_priority[development_priority],
|
131
|
+
'#' * (@avg_development_priority[development_priority] / PLOT_WIDTH)]
|
132
|
+
end
|
133
|
+
|
134
|
+
puts
|
135
|
+
puts 'Average First Reply Time by User Priority'
|
136
|
+
puts '___________________________________________'
|
137
|
+
puts 'Priority Reply-Time-Hours'
|
138
|
+
@avg_user_priority.keys.sort.each do |user_priority|
|
139
|
+
printf "%-10s %5d %s \n" % [user_priority, @avg_user_priority[user_priority],
|
140
|
+
'#' * (@avg_user_priority[user_priority] / PLOT_WIDTH)]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def save
|
145
|
+
CSV.open('solved_tickets_by_age.csv', 'wb') do |csv|
|
146
|
+
csv << ['Days', 'Ticket-Count']
|
147
|
+
end
|
148
|
+
@solved_tickets_by_age_log_scale.keys.each do |age|
|
149
|
+
CSV.open('solved_tickets_by_age.csv', 'a') do |csv|
|
150
|
+
csv << [age, @solved_tickets_by_age_log_scale[age]]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
CSV.open('unsolved_tickets_by_age.csv', 'wb') do |csv|
|
155
|
+
csv << ['Days', 'Ticket-Count']
|
156
|
+
end
|
157
|
+
@unsolved_tickets_by_age_log_scale.keys.each do |age|
|
158
|
+
CSV.open('unsolved_tickets_by_age.csv', 'a') do |csv|
|
159
|
+
csv << [age, @unsolved_tickets_by_age_log_scale[age]]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
CSV.open('avg_reply_time_priority.csv', 'wb') do |csv|
|
164
|
+
csv << ['Priority', 'Average-Reply-Time']
|
165
|
+
end
|
166
|
+
@avg_development_priority.keys.sort.each do |age|
|
167
|
+
CSV.open('avg_reply_time_priority.csv', 'a') do |csv|
|
168
|
+
csv << [age, @avg_development_priority[age]]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Interface with zendesk to access articles
|
2
|
+
|
3
|
+
class ZendeskArticle
|
4
|
+
def initialize(username, password, domain)
|
5
|
+
@root_uri = "https://#{domain}.zendesk.com/api/v2/help_center/en-us/"
|
6
|
+
@articles_uri = @root_uri + 'articles.json'
|
7
|
+
@sections_uri = @root_uri + 'sections.json'
|
8
|
+
@categories_uri = @root_uri + 'categories.json'
|
9
|
+
@username, @password = username, password
|
10
|
+
check_auth
|
11
|
+
end
|
12
|
+
|
13
|
+
def articles
|
14
|
+
@articles ||= begin
|
15
|
+
progressbar = ProgressBar.create(title: "#{raw_articles['count']} Articles", starting_at: 1, format: '%a |%b>>%i| %p%% %t', total: raw_articles['page_count'])
|
16
|
+
articles = raw_articles['articles']
|
17
|
+
|
18
|
+
(raw_articles['page_count'] - 1).times do |page|
|
19
|
+
progressbar.increment
|
20
|
+
articles += HTTParty.get("#{@articles_uri}?page=#{page + 2}", basic_auth)['articles']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
articles
|
24
|
+
end
|
25
|
+
|
26
|
+
def sections
|
27
|
+
@sections ||= begin
|
28
|
+
progressbar = ProgressBar.create(title: "#{raw_sections['count']} Sections", starting_at: 1, format: '%a |%b>>%i| %p%% %t', total: raw_sections['page_count'])
|
29
|
+
sections = raw_sections['sections']
|
30
|
+
|
31
|
+
(raw_sections['page_count'] - 1).times do |page|
|
32
|
+
progressbar.increment
|
33
|
+
sections += HTTParty.get("#{@sections_uri}?page=#{page + 2}", basic_auth)['sections']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
sections
|
37
|
+
end
|
38
|
+
|
39
|
+
def categories
|
40
|
+
@categories ||= begin
|
41
|
+
progressbar = ProgressBar.create(title: "#{raw_categories['count']} Categories", starting_at: 1, format: '%a |%b>>%i| %p%% %t', total: raw_categories['page_count'])
|
42
|
+
categories = raw_categories['categories']
|
43
|
+
|
44
|
+
(raw_categories['page_count'] - 1).times do |page|
|
45
|
+
progressbar.increment
|
46
|
+
categories += HTTParty.get("#{@categories_uri}?page=#{page + 2}", basic_auth)['categories']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
categories
|
50
|
+
end
|
51
|
+
|
52
|
+
def raw_articles
|
53
|
+
@raw_articles ||= HTTParty.get(@articles_uri, basic_auth)
|
54
|
+
end
|
55
|
+
|
56
|
+
def raw_sections
|
57
|
+
@raw_sections ||= HTTParty.get(@sections_uri, basic_auth)
|
58
|
+
end
|
59
|
+
|
60
|
+
def raw_categories
|
61
|
+
@raw_sections ||= HTTParty.get(@categories_uri, basic_auth)
|
62
|
+
end
|
63
|
+
|
64
|
+
def export_columns
|
65
|
+
%w(id category section title word_count draft promoted outdated html_url created_at updated_at)
|
66
|
+
end
|
67
|
+
|
68
|
+
def basic_auth
|
69
|
+
{
|
70
|
+
basic_auth: {
|
71
|
+
username: @username,
|
72
|
+
password: @password
|
73
|
+
}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def check_auth
|
78
|
+
response = HTTParty.get(@sections_uri, basic_auth)
|
79
|
+
unless response.code == 200
|
80
|
+
puts "Error #{response.code}: #{response.message}"
|
81
|
+
abort
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'httparty'
|
4
|
+
require 'ruby-progressbar'
|
5
|
+
require 'csv'
|
6
|
+
require 'pry'
|
7
|
+
require 'optparse'
|
8
|
+
require_relative 'metrics'
|
9
|
+
require_relative 'ticket'
|
10
|
+
|
11
|
+
class ZendeskTicket
|
12
|
+
|
13
|
+
attr_accessor :root_uri, :start_time, :tickets_uri, :domain, :username, :password
|
14
|
+
|
15
|
+
def initialize(username, password, domain)
|
16
|
+
@root_uri = "https://#{domain}.zendesk.com/api/v2/"
|
17
|
+
@start_time = Time.new('2016-01-01').to_i
|
18
|
+
@tickets_uri = @root_uri + "incremental/tickets.json?start_time=#{@start_time}"
|
19
|
+
@username, @password, @domain = username, password, domain
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
|
24
|
+
puts 'Checking authentication...'
|
25
|
+
check_auth
|
26
|
+
puts 'Authentication successful!'
|
27
|
+
puts
|
28
|
+
puts 'Envision Zendesk Tickets'
|
29
|
+
puts '------------------------'
|
30
|
+
puts '-> Retrieving Tickets...'
|
31
|
+
|
32
|
+
tickets_in = self.download_tickets
|
33
|
+
tickets = self.retrieve_fields(tickets_in)
|
34
|
+
metrics = Metrics.new(tickets)
|
35
|
+
metrics.graph
|
36
|
+
metrics.save
|
37
|
+
end
|
38
|
+
|
39
|
+
def download_tickets
|
40
|
+
@tickets ||= begin
|
41
|
+
|
42
|
+
first_page = HTTParty.get(@tickets_uri, basic_auth)
|
43
|
+
# puts first_page
|
44
|
+
tickets = first_page['tickets']
|
45
|
+
next_url = first_page['next_page']
|
46
|
+
count = first_page['count']
|
47
|
+
puts " Got: #{count}"
|
48
|
+
|
49
|
+
while count == 1000
|
50
|
+
next_page = HTTParty.get(next_url, basic_auth)
|
51
|
+
tickets += next_page['tickets']
|
52
|
+
next_url = next_page['next_page']
|
53
|
+
count = next_page['count']
|
54
|
+
puts " Got: #{count}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
tickets
|
58
|
+
end
|
59
|
+
|
60
|
+
def export_columns
|
61
|
+
%w(id type subject status user_priority development_priority company project
|
62
|
+
platform function satisfaction_rating created_at updated_at)
|
63
|
+
end
|
64
|
+
|
65
|
+
def metric_columns
|
66
|
+
%w(initially_assigned_at solved_at full_resolution_time_in_minutes
|
67
|
+
requester_wait_time_in_minutes reply_time_in_minutes)
|
68
|
+
end
|
69
|
+
|
70
|
+
def basic_auth
|
71
|
+
{
|
72
|
+
basic_auth: {
|
73
|
+
username: @username,
|
74
|
+
password: @password
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def check_auth
|
80
|
+
response = HTTParty.get(@tickets_uri, basic_auth)
|
81
|
+
unless response.code == 200
|
82
|
+
puts "Error #{response.code}: #{response.message}"
|
83
|
+
abort
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def retrieve_fields(tickets_in)
|
88
|
+
|
89
|
+
puts ' Total tickets = ' + tickets_in.count.to_s
|
90
|
+
puts
|
91
|
+
|
92
|
+
CSV.open('all_tickets.csv', 'wb') do |csv|
|
93
|
+
csv << self.export_columns + self.metric_columns
|
94
|
+
end
|
95
|
+
|
96
|
+
tickets = Array.new
|
97
|
+
|
98
|
+
print "Enter number of tickets (max of #{tickets_in.count.to_s}): "
|
99
|
+
number_of_tickets = gets.chomp.to_i
|
100
|
+
puts
|
101
|
+
|
102
|
+
progressbar = ProgressBar.create(title: "#{number_of_tickets} Tickets",
|
103
|
+
starting_at: 0, format: '%a |%b>>%i| %p%% %t', total: number_of_tickets)
|
104
|
+
|
105
|
+
tickets_in.first(number_of_tickets).each do |ticket|
|
106
|
+
CSV.open('all_tickets.csv', 'a') do |csv|
|
107
|
+
row = []
|
108
|
+
info = Hash.new
|
109
|
+
metrics_info = Hash.new
|
110
|
+
self.export_columns.each do |column|
|
111
|
+
case column
|
112
|
+
when 'type'
|
113
|
+
info['type'] = ticket['custom_fields'][0]['value']
|
114
|
+
row << info['type']
|
115
|
+
when 'user_priority'
|
116
|
+
info['user_priority'] = ticket['custom_fields'][1]['value']
|
117
|
+
row << info['user_priority']
|
118
|
+
when 'development_priority'
|
119
|
+
value = ticket['custom_fields'][2]['value']
|
120
|
+
if value
|
121
|
+
info['development_priority'] = "d#{value[-1]}" if value[-1].to_i > 0
|
122
|
+
else
|
123
|
+
info['development_priority'] = value
|
124
|
+
end
|
125
|
+
row << info['development_priority']
|
126
|
+
when 'company'
|
127
|
+
info['company'] = ticket['custom_fields'][3]['value']
|
128
|
+
row << info['company']
|
129
|
+
when 'project'
|
130
|
+
info['project'] = ticket['custom_fields'][4]['value']
|
131
|
+
row << info['project']
|
132
|
+
when 'platform'
|
133
|
+
info['platform'] = ticket['custom_fields'][5]['value']
|
134
|
+
row << info['platform']
|
135
|
+
when 'function'
|
136
|
+
info['function'] = ticket['custom_fields'][6]['value']
|
137
|
+
row << info['function']
|
138
|
+
when 'satisfaction_rating'
|
139
|
+
info['satisfaction_rating'] = ticket['satisfaction_rating']['score']
|
140
|
+
row << info['satisfaction_rating']
|
141
|
+
else
|
142
|
+
info[column] = ticket[column]
|
143
|
+
row << info[column]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
begin
|
148
|
+
metrics = HTTParty.get("#{@root_uri}tickets/#{ticket['id']}/metrics.json", self.basic_auth)
|
149
|
+
self.metric_columns.each do |column|
|
150
|
+
if metrics['ticket_metric']
|
151
|
+
case column
|
152
|
+
when 'solved_at', 'initially_assigned_at'
|
153
|
+
metrics_info[column] = metrics['ticket_metric'][column]
|
154
|
+
else
|
155
|
+
metrics_info[column] = metrics['ticket_metric'][column]['business']
|
156
|
+
end
|
157
|
+
row << metrics_info[column]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
rescue
|
161
|
+
retry
|
162
|
+
end
|
163
|
+
|
164
|
+
this = Ticket.new(info, metrics_info)
|
165
|
+
tickets << this
|
166
|
+
csv << row
|
167
|
+
progressbar.increment
|
168
|
+
end
|
169
|
+
end
|
170
|
+
tickets
|
171
|
+
end
|
172
|
+
end
|