qa_server 2.2.0 → 2.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/app/models/qa_server/performance_history.rb +164 -60
- data/app/models/qa_server/term_scenario.rb +1 -1
- data/app/prepends/prepended_linked_data/find_term.rb +2 -1
- data/app/prepends/prepended_linked_data/search_query.rb +1 -0
- data/app/presenters/concerns/qa_server/monitor_status/gruff_graph.rb +21 -0
- data/app/presenters/qa_server/monitor_status/current_status_presenter.rb +76 -0
- data/app/presenters/qa_server/monitor_status/history_presenter.rb +138 -0
- data/app/presenters/qa_server/monitor_status/performance_presenter.rb +253 -0
- data/app/presenters/qa_server/monitor_status_presenter.rb +23 -310
- data/app/views/qa_server/monitor_status/index.html.erb +38 -1
- data/config/locales/qa_server.en.yml +8 -1
- data/lib/generators/qa_server/templates/config/initializers/qa_server.rb +8 -0
- data/lib/qa_server/configuration.rb +20 -0
- data/lib/qa_server/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9a28076a2932c11c2dcfa1d833665d623ab3969
|
4
|
+
data.tar.gz: 0e5eeb1c0c5535029f60c8a5f1a4bead059e4bbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f891059ddd3ef9453bf857dbd7e5cc0ce5cab4beead2897b02efac28803f82a06fb00fb94ce3de487e30bb00f085049e14c80052a8c80565ae4931401c962f7b
|
7
|
+
data.tar.gz: e4c733a195568694b0bd9ed8fe7704aa62a3ab07c90c6ed2d3876869be33e439c68f28dc081c631182cb9b2dcba42a392b45420a5392a9079eb73c0e25c73040
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
# Provide access to the scenario_results_history database table which tracks specific scenario runs over time.
|
3
3
|
module QaServer
|
4
|
-
class PerformanceHistory < ActiveRecord::Base
|
4
|
+
class PerformanceHistory < ActiveRecord::Base # rubocop:disable Metrics/ClassLength
|
5
5
|
self.table_name = 'performance_history'
|
6
6
|
|
7
7
|
enum action: [:fetch, :search]
|
8
8
|
|
9
|
+
PERFORMANCE_ALL_KEY = :all_authorities
|
10
|
+
PERFORMANCE_STATS_KEY = :stats
|
11
|
+
PERFORMANCE_FOR_LIFETIME_KEY = :lifetime_stats
|
12
|
+
|
9
13
|
PERFORMANCE_FOR_DAY_KEY = :day
|
10
14
|
PERFORMANCE_BY_HOUR_KEY = :hour
|
11
15
|
|
@@ -15,16 +19,24 @@ module QaServer
|
|
15
19
|
PERFORMANCE_FOR_YEAR_KEY = :year
|
16
20
|
PERFORMANCE_BY_MONTH_KEY = :month
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
22
|
+
SUM_LOAD_TIME_KEY = :load_sum_ms
|
23
|
+
SUM_NORMALIZATION_TIME_KEY = :normalization_sum_ms
|
24
|
+
SUM_FULL_REQUEST_TIME_KEY = :full_request_sum_ms
|
25
|
+
MIN_LOAD_TIME_KEY = :load_min_ms
|
26
|
+
MIN_NORMALIZATION_TIME_KEY = :normalization_min_ms
|
27
|
+
MIN_FULL_REQUEST_TIME_KEY = :full_request_min_ms
|
28
|
+
MAX_LOAD_TIME_KEY = :load_max_ms
|
29
|
+
MAX_NORMALIZATION_TIME_KEY = :normalization_max_ms
|
30
|
+
MAX_FULL_REQUEST_TIME_KEY = :full_request_max_ms
|
31
|
+
AVG_LOAD_TIME_KEY = :load_avg_ms
|
32
|
+
AVG_NORMALIZATION_TIME_KEY = :normalization_avg_ms
|
33
|
+
AVG_FULL_REQUEST_TIME_KEY = :full_request_avg_ms
|
21
34
|
|
22
35
|
class << self
|
23
|
-
|
24
36
|
# Save a scenario result
|
25
37
|
# @param run_id [Integer] the run on which to gather statistics
|
26
38
|
# @param result [Hash] the scenario result to be saved
|
27
|
-
def save_result(dt_stamp:, authority:, action:, size_bytes:, load_time_ms:, normalization_time_ms:
|
39
|
+
def save_result(dt_stamp:, authority:, action:, size_bytes:, load_time_ms:, normalization_time_ms:) # rubocop:disable Metrics/ParameterLists
|
28
40
|
QaServer::PerformanceHistory.create(dt_stamp: dt_stamp,
|
29
41
|
authority: authority,
|
30
42
|
action: action,
|
@@ -36,115 +48,207 @@ module QaServer
|
|
36
48
|
# Performance data for a day, a month, and a year.
|
37
49
|
# @returns [Hash] performance statistics for the past 24 hours
|
38
50
|
# @example
|
39
|
-
# {
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
51
|
+
# { all_authorities:
|
52
|
+
# { lifetime_stats:
|
53
|
+
# { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }
|
54
|
+
# }
|
55
|
+
# { day:
|
56
|
+
# { 0: { hour: '1400', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
57
|
+
# 1: { hour: '1500', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
58
|
+
# 2: { hour: '1600', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
59
|
+
# ...,
|
60
|
+
# 23: { hour: 'NOW', load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
61
|
+
# }
|
62
|
+
# }
|
63
|
+
# { month:
|
64
|
+
# { 0: { day: '07-15-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
65
|
+
# 1: { day: '07-16-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
66
|
+
# 2: { day: '07-17-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
67
|
+
# ...,
|
68
|
+
# 29: { day: 'TODAY', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
69
|
+
# }
|
70
|
+
# }
|
71
|
+
# { year:
|
72
|
+
# { 0: { month: '09-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
73
|
+
# 1: { month: '10-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
74
|
+
# 2: { month: '11-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
75
|
+
# ...,
|
76
|
+
# 11: { month: '08-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
77
|
+
# }
|
78
|
+
# }
|
44
79
|
# }
|
45
80
|
def performance_data
|
46
81
|
data = {}
|
47
|
-
data[
|
48
|
-
|
49
|
-
|
82
|
+
data[PERFORMANCE_ALL_KEY] = {
|
83
|
+
PERFORMANCE_FOR_LIFETIME_KEY => lifetime,
|
84
|
+
PERFORMANCE_FOR_DAY_KEY => average_last_24_hours,
|
85
|
+
PERFORMANCE_FOR_MONTH_KEY => average_last_30_days,
|
86
|
+
PERFORMANCE_FOR_YEAR_KEY => average_last_12_months
|
87
|
+
}
|
50
88
|
data
|
51
89
|
end
|
52
90
|
|
53
91
|
private
|
54
92
|
|
93
|
+
# Get hourly average for the past 24 hours.
|
94
|
+
# @returns [Hash] performance statistics across all records
|
95
|
+
# @example
|
96
|
+
# { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }
|
97
|
+
def lifetime
|
98
|
+
records = PerformanceHistory.all
|
99
|
+
calculate_stats(records)
|
100
|
+
end
|
101
|
+
|
55
102
|
# Get hourly average for the past 24 hours.
|
56
103
|
# @returns [Hash] performance statistics for the past 24 hours
|
57
104
|
# @example
|
58
|
-
# { 0: { hour: 1400, load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
59
|
-
# 1: { hour: 1500, load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
60
|
-
# 2: { hour: 1600, load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
105
|
+
# { 0: { hour: '1400', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
106
|
+
# 1: { hour: '1500', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
107
|
+
# 2: { hour: '1600', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
61
108
|
# ...,
|
62
|
-
# 23: { hour:
|
109
|
+
# 23: { hour: 'NOW', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
63
110
|
# }
|
64
111
|
def average_last_24_hours
|
65
|
-
start_hour = Time.now.beginning_of_hour - 23.
|
112
|
+
start_hour = Time.now.beginning_of_hour - 23.hours
|
66
113
|
avgs = {}
|
67
114
|
0.upto(23).each do |idx|
|
68
115
|
records = PerformanceHistory.where(dt_stamp: start_hour..start_hour.end_of_hour)
|
69
|
-
|
116
|
+
stats = calculate_stats(records)
|
70
117
|
data = {}
|
71
|
-
data[PERFORMANCE_BY_HOUR_KEY] =
|
72
|
-
data[
|
73
|
-
data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
|
74
|
-
data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
|
118
|
+
data[PERFORMANCE_BY_HOUR_KEY] = performance_by_hour_label(idx, start_hour)
|
119
|
+
data[PERFORMANCE_STATS_KEY] = stats
|
75
120
|
avgs[idx] = data
|
76
|
-
start_hour
|
121
|
+
start_hour += 1.hour
|
77
122
|
end
|
78
123
|
avgs
|
79
124
|
end
|
80
125
|
|
126
|
+
def performance_by_hour_label(idx, start_hour)
|
127
|
+
if idx == 23
|
128
|
+
I18n.t('qa_server.monitor_status.performance.now')
|
129
|
+
elsif ((idx + 1) % 2).zero?
|
130
|
+
(start_hour.hour * 100).to_s
|
131
|
+
else
|
132
|
+
""
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
81
136
|
# Get daily average for the past 30 days.
|
82
137
|
# @returns [Hash] performance statistics for the past 30 days
|
83
138
|
# @example
|
84
|
-
# { 0: { day: '07-15-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
85
|
-
# 1: { day: '07-16-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
86
|
-
# 2: { day: '07-17-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
139
|
+
# { 0: { day: '07-15-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
140
|
+
# 1: { day: '07-16-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
141
|
+
# 2: { day: '07-17-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
87
142
|
# ...,
|
88
|
-
# 29: { day: '
|
143
|
+
# 29: { day: 'TODAY', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
89
144
|
# }
|
90
145
|
def average_last_30_days
|
91
|
-
start_day = Time.now.beginning_of_day - 29.
|
146
|
+
start_day = Time.now.beginning_of_day - 29.days
|
92
147
|
avgs = {}
|
93
148
|
0.upto(29).each do |idx|
|
94
149
|
records = PerformanceHistory.where(dt_stamp: start_day..start_day.end_of_day)
|
95
|
-
|
150
|
+
stats = calculate_stats(records)
|
96
151
|
data = {}
|
97
|
-
data[PERFORMANCE_BY_DAY_KEY] =
|
98
|
-
data[
|
99
|
-
data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
|
100
|
-
data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
|
152
|
+
data[PERFORMANCE_BY_DAY_KEY] = performance_by_day_label(idx, start_day)
|
153
|
+
data[PERFORMANCE_STATS_KEY] = stats
|
101
154
|
avgs[idx] = data
|
102
|
-
start_day
|
155
|
+
start_day += 1.day
|
103
156
|
end
|
104
157
|
avgs
|
105
158
|
end
|
106
159
|
|
160
|
+
def performance_by_day_label(idx, start_day)
|
161
|
+
if idx == 29
|
162
|
+
I18n.t('qa_server.monitor_status.performance.today')
|
163
|
+
elsif ((idx + 1) % 5).zero?
|
164
|
+
start_day.strftime("%m-%d")
|
165
|
+
else
|
166
|
+
""
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
107
170
|
# Get daily average for the past 12 months.
|
108
171
|
# @returns [Hash] performance statistics for the past 12 months
|
109
172
|
# @example
|
110
|
-
# { 0: { month: '09-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
111
|
-
# 1: { month: '10-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
112
|
-
# 2: { month: '11-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
173
|
+
# { 0: { month: '09-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
174
|
+
# 1: { month: '10-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
175
|
+
# 2: { month: '11-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }},
|
113
176
|
# ...,
|
114
|
-
# 11: { month: '08-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2,
|
177
|
+
# 11: { month: '08-2019', stats: { load_avg_ms: 12.3, normalization_avg_ms: 4.2, full_request_avg_ms: 16.5, etc. }}
|
115
178
|
# }
|
116
179
|
def average_last_12_months
|
117
|
-
start_month = Time.now.beginning_of_month - 11.
|
180
|
+
start_month = Time.now.beginning_of_month - 11.months
|
118
181
|
avgs = {}
|
119
182
|
0.upto(11).each do |idx|
|
120
183
|
records = PerformanceHistory.where(dt_stamp: start_month..start_month.end_of_month)
|
121
|
-
|
184
|
+
stats = calculate_stats(records)
|
122
185
|
data = {}
|
123
|
-
data[PERFORMANCE_BY_MONTH_KEY] =
|
124
|
-
data[
|
125
|
-
data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
|
126
|
-
data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
|
186
|
+
data[PERFORMANCE_BY_MONTH_KEY] = start_month.strftime("%m-%Y")
|
187
|
+
data[PERFORMANCE_STATS_KEY] = stats
|
127
188
|
avgs[idx] = data
|
128
|
-
start_month
|
189
|
+
start_month += 1.month
|
129
190
|
end
|
130
191
|
avgs
|
131
192
|
end
|
132
193
|
|
133
|
-
def
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
sum_combined_times = 0
|
194
|
+
def calculate_stats(records)
|
195
|
+
stats = init_stats
|
196
|
+
return stats if records.count.zero?
|
197
|
+
first = true
|
138
198
|
records.each do |record|
|
139
|
-
|
140
|
-
|
141
|
-
|
199
|
+
update_sum_stats(stats, record)
|
200
|
+
update_min_stats(stats, record)
|
201
|
+
update_max_stats(stats, record)
|
202
|
+
first = false
|
142
203
|
end
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
204
|
+
calculate_avg_stats(stats, records)
|
205
|
+
stats
|
206
|
+
end
|
207
|
+
|
208
|
+
MIN_STARTING_TIME = 999_999_999
|
209
|
+
def init_stats
|
210
|
+
stats = {}
|
211
|
+
stats[SUM_LOAD_TIME_KEY] = 0
|
212
|
+
stats[SUM_NORMALIZATION_TIME_KEY] = 0
|
213
|
+
stats[SUM_FULL_REQUEST_TIME_KEY] = 0
|
214
|
+
stats[AVG_LOAD_TIME_KEY] = 0
|
215
|
+
stats[AVG_NORMALIZATION_TIME_KEY] = 0
|
216
|
+
stats[AVG_FULL_REQUEST_TIME_KEY] = 0
|
217
|
+
stats[MIN_LOAD_TIME_KEY] = MIN_STARTING_TIME
|
218
|
+
stats[MIN_NORMALIZATION_TIME_KEY] = MIN_STARTING_TIME
|
219
|
+
stats[MIN_FULL_REQUEST_TIME_KEY] = MIN_STARTING_TIME
|
220
|
+
stats[MAX_LOAD_TIME_KEY] = 0
|
221
|
+
stats[MAX_NORMALIZATION_TIME_KEY] = 0
|
222
|
+
stats[MAX_FULL_REQUEST_TIME_KEY] = 0
|
223
|
+
stats
|
224
|
+
end
|
225
|
+
|
226
|
+
def update_sum_stats(stats, record)
|
227
|
+
stats[SUM_LOAD_TIME_KEY] += record.load_time_ms
|
228
|
+
stats[SUM_NORMALIZATION_TIME_KEY] += record.normalization_time_ms
|
229
|
+
stats[SUM_FULL_REQUEST_TIME_KEY] += full_request_time_ms(record)
|
230
|
+
end
|
231
|
+
|
232
|
+
def update_min_stats(stats, record)
|
233
|
+
stats[MIN_LOAD_TIME_KEY] = [stats[MIN_LOAD_TIME_KEY], record.load_time_ms].min
|
234
|
+
stats[MIN_NORMALIZATION_TIME_KEY] = [stats[MIN_NORMALIZATION_TIME_KEY], record.normalization_time_ms].min
|
235
|
+
stats[MIN_FULL_REQUEST_TIME_KEY] = [stats[MIN_FULL_REQUEST_TIME_KEY], full_request_time_ms(record)].min
|
236
|
+
end
|
237
|
+
|
238
|
+
def update_max_stats(stats, record)
|
239
|
+
stats[MAX_LOAD_TIME_KEY] = [stats[MAX_LOAD_TIME_KEY], record.load_time_ms].max
|
240
|
+
stats[MAX_NORMALIZATION_TIME_KEY] = [stats[MAX_NORMALIZATION_TIME_KEY], record.normalization_time_ms].max
|
241
|
+
stats[MAX_FULL_REQUEST_TIME_KEY] = [stats[MAX_FULL_REQUEST_TIME_KEY], full_request_time_ms(record)].max
|
242
|
+
end
|
243
|
+
|
244
|
+
def calculate_avg_stats(stats, records)
|
245
|
+
stats[AVG_LOAD_TIME_KEY] = stats[SUM_LOAD_TIME_KEY] / records.count
|
246
|
+
stats[AVG_NORMALIZATION_TIME_KEY] = stats[SUM_NORMALIZATION_TIME_KEY] / records.count
|
247
|
+
stats[AVG_FULL_REQUEST_TIME_KEY] = stats[SUM_FULL_REQUEST_TIME_KEY] / records.count
|
248
|
+
end
|
249
|
+
|
250
|
+
def full_request_time_ms(record)
|
251
|
+
record.load_time_ms + record.normalization_time_ms
|
148
252
|
end
|
149
253
|
end
|
150
254
|
end
|
@@ -42,7 +42,7 @@ module QaServer
|
|
42
42
|
|
43
43
|
def prefix_for_url(action)
|
44
44
|
subauth = "/#{subauthority_name}" if subauthority_name.present?
|
45
|
-
|
45
|
+
"#{QaServer::Engine.qa_engine_mount}/#{action}/linked_data/#{authority_name.downcase}#{subauth}"
|
46
46
|
end
|
47
47
|
|
48
48
|
# Convert identifier into URL safe version with encoding if needed.
|
@@ -1,7 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module PrependedLinkedData::FindTerm
|
2
3
|
# Override Qa::Authorities::LinkedData::FindTerm#find method
|
3
4
|
# @return [Hash] single term results in requested format
|
4
|
-
def find(id, language: nil, replacements: {}, subauth: nil, format: nil, jsonld: false, performance_data: false) # rubocop:disable Metrics/ParameterLists
|
5
|
+
def find(id, language: nil, replacements: {}, subauth: nil, format: nil, jsonld: false, performance_data: false) # rubocop:disable Metrics/ParameterLists
|
5
6
|
saved_performance_data = performance_data
|
6
7
|
performance_data = true
|
7
8
|
full_results = super
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'fileutils'
|
3
|
+
require 'gruff'
|
4
|
+
|
5
|
+
# This module include provides graph methods used by all monitor status presenters working with graphs
|
6
|
+
module QaServer::MonitorStatus
|
7
|
+
module GruffGraph
|
8
|
+
private
|
9
|
+
|
10
|
+
def graph_relative_path
|
11
|
+
File.join('qa_server', 'charts')
|
12
|
+
end
|
13
|
+
|
14
|
+
def graph_full_path(graph_filename)
|
15
|
+
path = Rails.root.join('app', 'assets', 'images', graph_relative_path)
|
16
|
+
FileUtils.mkdir_p path
|
17
|
+
File.join(path, graph_filename)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
# frozen_string_literal: true
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# This presenter class provides data related to last test run as needed by the view that monitors status of authorities.
|
3
|
+
module QaServer::MonitorStatus
|
4
|
+
class CurrentStatusPresenter
|
5
|
+
# @param current_summary [ScenarioRunSummary] summary status of the latest run of test scenarios
|
6
|
+
# @param current_data [Array<Hash>] current set of failures for the latest test run, if any
|
7
|
+
def initialize(current_summary:, current_failure_data:)
|
8
|
+
@current_summary = current_summary
|
9
|
+
@current_failure_data = current_failure_data
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [String] date of last test run
|
13
|
+
def last_updated
|
14
|
+
@current_summary.run_dt_stamp.in_time_zone("Eastern Time (US & Canada)").strftime("%m/%d/%y - %I:%M %p")
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [String] date of first recorded test run
|
18
|
+
def first_updated
|
19
|
+
QaServer::ScenarioRunRegistry.first.dt_stamp.in_time_zone("Eastern Time (US & Canada)").strftime("%m/%d/%y - %I:%M %p")
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Integer] number of loaded authorities
|
23
|
+
def authorities_count
|
24
|
+
@current_summary.authority_count
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Integer] number of authorities with failing tests in the latest test run
|
28
|
+
def failing_authorities_count
|
29
|
+
@current_failure_data.map { |f| f[:authority_name] }.uniq.count
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [String] css style class representing whether all tests passed or any failed
|
33
|
+
def authorities_count_style
|
34
|
+
failures? ? 'status-bad' : 'status-good'
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Integer] number of tests in the latest test run
|
38
|
+
def tests_count
|
39
|
+
@current_summary.total_scenario_count
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Integer] number of passing tests in the latest test run
|
43
|
+
def passing_tests_count
|
44
|
+
@current_summary.passing_scenario_count
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Integer] number of failing tests in the latest test run
|
48
|
+
def failing_tests_count
|
49
|
+
@current_summary.failing_scenario_count
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [String] css style class representing whether all tests passed or any failed
|
53
|
+
def failing_tests_style
|
54
|
+
failures? ? 'summary-status-bad' : 'status-good'
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Array<Hash>] A list of failures data in the latest test run, if any
|
58
|
+
# @example
|
59
|
+
# [ { status: :FAIL,
|
60
|
+
# status_label: 'X',
|
61
|
+
# authority_name: 'LOCNAMES_LD4L_CACHE',
|
62
|
+
# subauthority_name: 'person',
|
63
|
+
# service: 'ld4l_cache',
|
64
|
+
# action: 'search',
|
65
|
+
# url: '/qa/search/linked_data/locnames_ld4l_cache/person?q=mark twain&maxRecords=4',
|
66
|
+
# err_message: 'Exception: Something went wrong.' }, ... ]
|
67
|
+
def failures
|
68
|
+
@current_failure_data
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [Boolean] true if failure data exists for the latest test run; otherwise false
|
72
|
+
def failures?
|
73
|
+
failing_tests_count.positive?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# This presenter class provides historical testing data needed by the view that monitors status of authorities.
|
3
|
+
module QaServer::MonitorStatus
|
4
|
+
class HistoryPresenter
|
5
|
+
HISTORICAL_AUTHORITY_NAME_IDX = 0
|
6
|
+
HISTORICAL_FAILURE_COUNT_IDX = 1
|
7
|
+
HISTORICAL_PASSING_COUNT_IDX = 2
|
8
|
+
|
9
|
+
include QaServer::MonitorStatus::GruffGraph
|
10
|
+
|
11
|
+
# @param historical_summary_data [Array<Hash>] summary of past failuring runs per authority to drive chart
|
12
|
+
def initialize(historical_summary_data:)
|
13
|
+
@historical_summary_data = historical_summary_data
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Array<Hash>] historical test data to be displayed (authname, failing, passing)
|
17
|
+
# @example
|
18
|
+
# [ [ 'agrovoc', 0, 24 ],
|
19
|
+
# [ 'geonames_ld4l_cache', 2, 22 ] ... ]
|
20
|
+
def historical_summary
|
21
|
+
@historical_summary_data
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Boolean] true if historical test data exists; otherwise false
|
25
|
+
def history?
|
26
|
+
return true if @historical_summary_data.present?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def historical_graph
|
31
|
+
# g = Gruff::SideStackedBar.new('800x400')
|
32
|
+
g = Gruff::SideStackedBar.new
|
33
|
+
historical_graph_theme(g)
|
34
|
+
g.title = ''
|
35
|
+
historical_data = rework_historical_data_for_gruff
|
36
|
+
g.labels = historical_data[0]
|
37
|
+
g.data('Fail', historical_data[1])
|
38
|
+
g.data('Pass', historical_data[2])
|
39
|
+
g.write historical_graph_full_path
|
40
|
+
File.join(graph_relative_path, historical_graph_filename)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [String] the name of the css style class to use for the status cell based on the status of the scenario test.
|
44
|
+
def status_style_class(status)
|
45
|
+
"status-#{status[:status]}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [String] the name of the css style class to use for the status cell based on the status of the scenario test.
|
49
|
+
def status_label(status)
|
50
|
+
case status[:status]
|
51
|
+
when :good
|
52
|
+
QaServer::ScenarioRunHistory::GOOD_MARKER
|
53
|
+
when :bad
|
54
|
+
QaServer::ScenarioRunHistory::BAD_MARKER
|
55
|
+
when :unknown
|
56
|
+
QaServer::ScenarioRunHistory::UNKNOWN_MARKER
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def historical_data_authority_name(historical_entry)
|
61
|
+
historical_entry[HISTORICAL_AUTHORITY_NAME_IDX]
|
62
|
+
end
|
63
|
+
|
64
|
+
def days_authority_passing(historical_entry)
|
65
|
+
historical_entry[HISTORICAL_PASSING_COUNT_IDX]
|
66
|
+
end
|
67
|
+
|
68
|
+
def days_authority_failing(historical_entry)
|
69
|
+
historical_entry[HISTORICAL_FAILURE_COUNT_IDX]
|
70
|
+
end
|
71
|
+
|
72
|
+
def days_authority_tested(historical_entry)
|
73
|
+
days_authority_passing(historical_entry) + days_authority_failing(historical_entry)
|
74
|
+
end
|
75
|
+
|
76
|
+
def percent_authority_failing(historical_entry)
|
77
|
+
days_authority_failing(historical_entry).to_f / days_authority_tested(historical_entry)
|
78
|
+
end
|
79
|
+
|
80
|
+
def percent_authority_failing_str(historical_entry)
|
81
|
+
"#{percent_authority_failing(historical_entry) * 100}%"
|
82
|
+
end
|
83
|
+
|
84
|
+
def failure_style_class(historical_entry)
|
85
|
+
return "status-neutral" if days_authority_failing(historical_entry) <= 0
|
86
|
+
return "status-unknown" if percent_authority_failing(historical_entry) < 0.1
|
87
|
+
"status-bad"
|
88
|
+
end
|
89
|
+
|
90
|
+
def passing_style_class(historical_entry)
|
91
|
+
return "status-bad" if days_authority_passing(historical_entry) <= 0
|
92
|
+
"status-good"
|
93
|
+
end
|
94
|
+
|
95
|
+
def display_history_details?
|
96
|
+
display_historical_graph? || display_historical_datatable?
|
97
|
+
end
|
98
|
+
|
99
|
+
def display_historical_graph?
|
100
|
+
QaServer.config.display_historical_graph?
|
101
|
+
end
|
102
|
+
|
103
|
+
def display_historical_datatable?
|
104
|
+
QaServer.config.display_historical_datatable?
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def historical_graph_theme(g)
|
110
|
+
g.theme_pastel
|
111
|
+
g.colors = ['#ffcccc', '#ccffcc']
|
112
|
+
g.marker_font_size = 12
|
113
|
+
g.x_axis_increment = 10
|
114
|
+
end
|
115
|
+
|
116
|
+
def historical_graph_full_path
|
117
|
+
graph_full_path(historical_graph_filename)
|
118
|
+
end
|
119
|
+
|
120
|
+
def historical_graph_filename
|
121
|
+
'historical_side_stacked_bar.png'
|
122
|
+
end
|
123
|
+
|
124
|
+
def rework_historical_data_for_gruff
|
125
|
+
labels = {}
|
126
|
+
pass_data = []
|
127
|
+
fail_data = []
|
128
|
+
i = 0
|
129
|
+
historical_summary.each do |data|
|
130
|
+
labels[i] = data[0]
|
131
|
+
i += 1
|
132
|
+
fail_data << data[1]
|
133
|
+
pass_data << data[2]
|
134
|
+
end
|
135
|
+
[labels, fail_data, pass_data]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|