qa_server 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +6 -0
  4. data/app/assets/stylesheets/qa_server/_fetch.scss +16 -0
  5. data/app/assets/stylesheets/qa_server/_monitor-status.scss +34 -0
  6. data/app/assets/stylesheets/qa_server/_qa_server.scss +1 -1
  7. data/app/assets/stylesheets/qa_server/_styles.scss +4 -0
  8. data/app/controllers/qa_server/fetch_controller.rb +60 -0
  9. data/app/controllers/qa_server/monitor_status_controller.rb +4 -2
  10. data/app/models/qa_server/performance_history.rb +151 -0
  11. data/app/prepends/prepended_linked_data/find_term.rb +16 -0
  12. data/app/prepends/prepended_linked_data/search_query.rb +16 -0
  13. data/app/presenters/qa_server/fetch_presenter.rb +65 -0
  14. data/app/presenters/qa_server/monitor_status_presenter.rb +114 -8
  15. data/app/presenters/qa_server/navmenu_presenter.rb +1 -0
  16. data/app/services/qa_server/authority_loader_service.rb +35 -29
  17. data/app/views/layouts/qa_server.html.erb +3 -1
  18. data/app/views/qa_server/fetch/index.html.erb +62 -0
  19. data/app/views/qa_server/monitor_status/index.html.erb +62 -2
  20. data/config/locales/qa_server.en.yml +24 -0
  21. data/config/routes.rb +1 -0
  22. data/lib/generators/qa_server/config_generator.rb +9 -0
  23. data/lib/generators/qa_server/templates/config/authorities/linked_data/DISABLED/scenarios/mesh_bioportal_ld4l_cache_validation.yml +2 -0
  24. data/lib/generators/qa_server/templates/config/authorities/linked_data/getty_aat_ld4l_cache.json +1 -1
  25. data/lib/generators/qa_server/templates/config/authorities/linked_data/getty_tgn_ld4l_cache.json +1 -1
  26. data/lib/generators/qa_server/templates/config/authorities/linked_data/getty_ulan_ld4l_cache.json +1 -1
  27. data/lib/generators/qa_server/templates/config/authorities/linked_data/locdemographics_ld4l_cache.json +110 -3
  28. data/lib/generators/qa_server/templates/config/authorities/linked_data/locgenres_ld4l_cache.json +71 -25
  29. data/lib/generators/qa_server/templates/config/authorities/linked_data/locsubjects_ld4l_cache.json +76 -12
  30. data/lib/generators/qa_server/templates/config/authorities/linked_data/nalt_ld4l_cache.json +2 -4
  31. data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/loc_direct_validation.yml +3 -0
  32. data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/loc_validation.yml +6 -0
  33. data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/locdemographics_ld4l_cache_validation.yml +1 -0
  34. data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/locgenres_ld4l_cache_validation.yml +13 -14
  35. data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/locsubjects_ld4l_cache_validation.yml +1 -18
  36. data/lib/generators/qa_server/templates/config/authorities/linked_data/scenarios/oclcfast_direct_validation.yml +10 -10
  37. data/lib/generators/qa_server/templates/db/migrate/20190813045549_create_performance_history.rb.erb +12 -0
  38. data/lib/qa_server/version.rb +1 -1
  39. metadata +10 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca8062bd52624e0fe1021b65859073d5bb96e5b2
4
- data.tar.gz: ec6ab1c6ee67b9afdaf7c8d9838fa0c925af07cb
3
+ metadata.gz: 6b1aa8683845f3afeadc0dd3b26a77b56f881fed
4
+ data.tar.gz: 4a38cff642e17ba1be32215b8d89f840963bf1f7
5
5
  SHA512:
6
- metadata.gz: fe16865048f7079f8f3019a5dd07e258468f0d861fcd55e21a8b36cac7de60765579f1f573505a850e4f41c2e2bfe0362114e057ab488f004b5f02ea97051242
7
- data.tar.gz: d8e2d935c827ea495cb4e2dbee4481035d9254e04e340d5a9937e416f61ae6762441ecb4fc31e1d0b381e518f48e63b1ff51abc50e44a8df6642e41de4bce39e
6
+ metadata.gz: 1fd0e79db6c61c2f696eb04eee64f18ef0e82393eba5cc57b13b9641158f235a8d28008ee85f6cc86a1c6641d4d7663e0ffbd8c5a0b1f0c6137b4e5543820af8
7
+ data.tar.gz: 7fbb5e3f7c86a6e26b7dc891a6306cb618b038ddecaa29b0c405a5bf31c3c29049560c0591902e20164ac9715ede061ed3252281e16f6256afcc3bce5620f32a
data/.gitignore CHANGED
@@ -38,6 +38,7 @@ script/log
38
38
  /public/documents
39
39
  /public/assets
40
40
  /public/sitemap.xml.gz
41
+ /app/assets/images/qa_server/charts/*
41
42
 
42
43
  # ignore renderer file
43
44
  # shows the template being rendered in the html source
@@ -1,3 +1,9 @@
1
+ ### 2.2.0 (2019-08-15)
2
+
3
+ * add performance graphs to monitor status page
4
+ * add ability to fetch a term through the UI
5
+ * update authority configs to latest versions
6
+
1
7
  ### 2.1.0 (2019-06-10)
2
8
 
3
9
  * remove context from mesh_nlm; add direct match to locnames
@@ -0,0 +1,16 @@
1
+ div.results-section {
2
+ margin-top: 50px;
3
+ }
4
+
5
+ div.results-panel {
6
+ padding: 50px;
7
+ }
8
+
9
+ .submit_button {
10
+ padding-top: 5px;
11
+ padding-bottom: 5px;
12
+ padding-left: 15px;
13
+ padding-right: 15px;
14
+ background-color: #303d4d;
15
+ color: whitesmoke;
16
+ }
@@ -11,3 +11,37 @@ p.status-update-dtstamp {
11
11
  color: darkred;
12
12
  font-style: italic;
13
13
  }
14
+
15
+ div.right-menu-section {
16
+ padding-top: 50px;
17
+ }
18
+
19
+ ul.right-menu {
20
+ list-style-type: none;
21
+ li.clickable {
22
+ display: inline;
23
+ padding-right: 30px;
24
+ color: #337ab7;
25
+ }
26
+ li.clickable:hover {
27
+ color: #23527c;
28
+ text-decoration: underline;
29
+ }
30
+ li.selected {
31
+ display: inline;
32
+ padding-right: 30px;
33
+ font-weight: bold;
34
+ }
35
+ }
36
+
37
+ div#performance-by-the-hour {
38
+ display: block;
39
+ }
40
+
41
+ div#performance-by-the-day {
42
+ display: none;
43
+ }
44
+
45
+ div#performance-by-the-month {
46
+ display: none;
47
+ }
@@ -1,5 +1,5 @@
1
1
  @import 'qa_server/styles', 'qa_server/header', 'qa_server/footer', 'qa_server/home-page', 'qa_server/usage',
2
- 'qa_server/authorities', 'qa_server/check-status', 'qa_server/monitor-status';
2
+ 'qa_server/authorities', 'qa_server/check-status', 'qa_server/monitor-status', 'qa_server/fetch';
3
3
 
4
4
 
5
5
 
@@ -29,3 +29,7 @@ hr {
29
29
  h4 {
30
30
  margin-top: 40px;
31
31
  }
32
+
33
+ .alert-error {
34
+ color: red;
35
+ }
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ # Controller for Check Status header menu item
3
+ module QaServer
4
+ class FetchController < ApplicationController
5
+ layout 'qa_server'
6
+
7
+ class_attribute :presenter_class,
8
+ :lister_class
9
+
10
+ self.presenter_class = QaServer::FetchPresenter
11
+ self.lister_class = QaServer::AuthorityListerService
12
+
13
+ # Sets up presenter with data to display in the UI
14
+ def index
15
+ flash[:error] = "Authority is required." if uri? && !authority_name?
16
+ @presenter = presenter_class.new(authorities_list: authorities_list,
17
+ authority: authority_name,
18
+ uri: uri,
19
+ format: format,
20
+ term_results: term_results)
21
+ end
22
+
23
+ private
24
+
25
+ def authorities_list
26
+ @authorities_list ||= lister_class.authorities_list
27
+ end
28
+
29
+ # @return [Qa::Authorities::LinkedData::GenericAuthority] the instance of the QA authority
30
+ def authority
31
+ return unless authority_name?
32
+ @authority ||= QaServer::AuthorityLoaderService.load(authority_name: authority_name)
33
+ end
34
+
35
+ def uri?
36
+ uri.present?
37
+ end
38
+
39
+ def uri
40
+ @uri ||= params.key?(:uri) ? params[:uri] : nil
41
+ end
42
+
43
+ def authority_name?
44
+ authority_name.present?
45
+ end
46
+
47
+ def authority_name
48
+ @authority_name ||= params.key?(:authority) ? params[:authority].downcase : nil
49
+ end
50
+
51
+ def format
52
+ @format ||= params.key?(:results_format) ? params[:results_format] : 'json'
53
+ end
54
+
55
+ def term_results
56
+ return unless authority_name? && uri?
57
+ @term_results = authority.find(uri, format: format)
58
+ end
59
+ end
60
+ end
@@ -4,11 +4,13 @@ module QaServer
4
4
  class MonitorStatusController < QaServer::AuthorityValidationController
5
5
  class_attribute :presenter_class,
6
6
  :scenario_run_registry_class,
7
- :scenario_history_class
7
+ :scenario_history_class,
8
+ :performance_history_class
8
9
 
9
10
  self.presenter_class = QaServer::MonitorStatusPresenter
10
11
  self.scenario_run_registry_class = QaServer::ScenarioRunRegistry
11
12
  self.scenario_history_class = QaServer::ScenarioRunHistory
13
+ self.performance_history_class = QaServer::PerformanceHistory
12
14
 
13
15
  # Sets up presenter with data to display in the UI
14
16
  def index
@@ -20,7 +22,7 @@ module QaServer
20
22
  @presenter = presenter_class.new(current_summary: latest_summary,
21
23
  current_failure_data: latest_failures,
22
24
  historical_summary_data: historical_summary_data,
23
- performance_data: [])
25
+ performance_data: performance_history_class.performance_data)
24
26
  render 'index', status: :internal_server_error if latest_summary.failing_authority_count.positive?
25
27
  end
26
28
 
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+ # Provide access to the scenario_results_history database table which tracks specific scenario runs over time.
3
+ module QaServer
4
+ class PerformanceHistory < ActiveRecord::Base
5
+ self.table_name = 'performance_history'
6
+
7
+ enum action: [:fetch, :search]
8
+
9
+ PERFORMANCE_FOR_DAY_KEY = :day
10
+ PERFORMANCE_BY_HOUR_KEY = :hour
11
+
12
+ PERFORMANCE_FOR_MONTH_KEY = :month
13
+ PERFORMANCE_BY_DAY_KEY = :day
14
+
15
+ PERFORMANCE_FOR_YEAR_KEY = :year
16
+ PERFORMANCE_BY_MONTH_KEY = :month
17
+
18
+ LOAD_TIME_KEY = :load_avg_ms
19
+ NORMALIZATION_TIME_KEY = :normalization_avg_ms
20
+ COMBINED_TIME_KEY = :combined_avg_ms
21
+
22
+ class << self
23
+
24
+ # Save a scenario result
25
+ # @param run_id [Integer] the run on which to gather statistics
26
+ # @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: )
28
+ QaServer::PerformanceHistory.create(dt_stamp: dt_stamp,
29
+ authority: authority,
30
+ action: action,
31
+ size_bytes: size_bytes,
32
+ load_time_ms: load_time_ms,
33
+ normalization_time_ms: normalization_time_ms)
34
+ end
35
+
36
+ # Performance data for a day, a month, and a year.
37
+ # @returns [Hash] performance statistics for the past 24 hours
38
+ # @example
39
+ # { 0: { hour: 1400, load_avg_ms: 12.3, normalization_avg_ms: 4.2 },
40
+ # 1: { hour: 1500, load_avg_ms: 12.3, normalization_avg_ms: 4.2 },
41
+ # 2: { hour: 1600, load_avg_ms: 12.3, normalization_avg_ms: 4.2 },
42
+ # ...,
43
+ # 23: { hour: 1300, load_avg_ms: 12.3, normalization_avg_ms: 4.2 }
44
+ # }
45
+ def performance_data
46
+ data = {}
47
+ data[PERFORMANCE_FOR_DAY_KEY] = average_last_24_hours
48
+ data[PERFORMANCE_FOR_MONTH_KEY] = average_last_30_days
49
+ data[PERFORMANCE_FOR_YEAR_KEY] = average_last_12_months
50
+ data
51
+ end
52
+
53
+ private
54
+
55
+ # Get hourly average for the past 24 hours.
56
+ # @returns [Hash] performance statistics for the past 24 hours
57
+ # @example
58
+ # { 0: { hour: 1400, load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
59
+ # 1: { hour: 1500, load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
60
+ # 2: { hour: 1600, load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
61
+ # ...,
62
+ # 23: { hour: 1300, load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 }
63
+ # }
64
+ def average_last_24_hours
65
+ start_hour = Time.now.beginning_of_hour - 23.hour
66
+ avgs = {}
67
+ 0.upto(23).each do |idx|
68
+ records = PerformanceHistory.where(dt_stamp: start_hour..start_hour.end_of_hour)
69
+ averages = calculate_averages(records)
70
+ data = {}
71
+ data[PERFORMANCE_BY_HOUR_KEY] = idx == 23 ? I18n.t('qa_server.monitor_status.performance.now') : ((idx + 1) % 2 == 0 ? (start_hour.hour * 100).to_s : "")
72
+ data[LOAD_TIME_KEY] = averages[:avg_load_time_ms]
73
+ data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
74
+ data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
75
+ avgs[idx] = data
76
+ start_hour = start_hour + 1.hour
77
+ end
78
+ avgs
79
+ end
80
+
81
+ # Get daily average for the past 30 days.
82
+ # @returns [Hash] performance statistics for the past 30 days
83
+ # @example
84
+ # { 0: { day: '07-15-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
85
+ # 1: { day: '07-16-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
86
+ # 2: { day: '07-17-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
87
+ # ...,
88
+ # 29: { day: '08-13-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 }
89
+ # }
90
+ def average_last_30_days
91
+ start_day = Time.now.beginning_of_day - 29.day
92
+ avgs = {}
93
+ 0.upto(29).each do |idx|
94
+ records = PerformanceHistory.where(dt_stamp: start_day..start_day.end_of_day)
95
+ averages = calculate_averages(records)
96
+ data = {}
97
+ data[PERFORMANCE_BY_DAY_KEY] = idx == 29 ? I18n.t('qa_server.monitor_status.performance.today') : ((idx + 1) % 5 == 0 ? (start_day).strftime("%m-%d") : "")
98
+ data[LOAD_TIME_KEY] = averages[:avg_load_time_ms]
99
+ data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
100
+ data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
101
+ avgs[idx] = data
102
+ start_day = start_day + 1.day
103
+ end
104
+ avgs
105
+ end
106
+
107
+ # Get daily average for the past 12 months.
108
+ # @returns [Hash] performance statistics for the past 12 months
109
+ # @example
110
+ # { 0: { month: '09-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
111
+ # 1: { month: '10-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
112
+ # 2: { month: '11-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 },
113
+ # ...,
114
+ # 11: { month: '08-2019', load_avg_ms: 12.3, normalization_avg_ms: 4.2, combined_avg_ms: 16.5 }
115
+ # }
116
+ def average_last_12_months
117
+ start_month = Time.now.beginning_of_month - 11.month
118
+ avgs = {}
119
+ 0.upto(11).each do |idx|
120
+ records = PerformanceHistory.where(dt_stamp: start_month..start_month.end_of_month)
121
+ averages = calculate_averages(records)
122
+ data = {}
123
+ data[PERFORMANCE_BY_MONTH_KEY] = (start_month).strftime("%m-%Y")
124
+ data[LOAD_TIME_KEY] = averages[:avg_load_time_ms]
125
+ data[NORMALIZATION_TIME_KEY] = averages[:avg_normalization_time_ms]
126
+ data[COMBINED_TIME_KEY] = averages[:avg_combined_time_ms]
127
+ avgs[idx] = data
128
+ start_month = start_month + 1.month
129
+ end
130
+ avgs
131
+ end
132
+
133
+ def calculate_averages(records)
134
+ return { avg_load_time_ms: 0, avg_normalization_time_ms: 0, avg_combined_time_ms: 0 } if records.count.zero?
135
+ sum_load_times = 0
136
+ sum_normalization_times = 0
137
+ sum_combined_times = 0
138
+ records.each do |record|
139
+ sum_load_times += record.load_time_ms
140
+ sum_normalization_times += record.normalization_time_ms
141
+ sum_combined_times += (record.load_time_ms + record.normalization_time_ms)
142
+ end
143
+ {
144
+ avg_load_time_ms: sum_load_times / records.count,
145
+ avg_normalization_time_ms: sum_normalization_times / records.count,
146
+ avg_combined_time_ms: sum_combined_times / records.count
147
+ }
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,16 @@
1
+ module PrependedLinkedData::FindTerm
2
+ # Override Qa::Authorities::LinkedData::FindTerm#find method
3
+ # @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, Metrics/MethodLength
5
+ saved_performance_data = performance_data
6
+ performance_data = true
7
+ full_results = super
8
+ QaServer::PerformanceHistory.save_result(dt_stamp: Time.now,
9
+ authority: authority_name,
10
+ action: 'fetch',
11
+ size_bytes: full_results[:performance][:fetched_bytes],
12
+ load_time_ms: (full_results[:performance][:fetch_time_s] * 1000),
13
+ normalization_time_ms: (full_results[:performance][:normalization_time_s] * 1000))
14
+ saved_performance_data ? full_results : full_results[:results]
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module PrependedLinkedData::SearchQuery
2
+ # Override Qa::Authorities::LinkedData::SearchQuery#search method
3
+ # @return [String] json results for search query
4
+ def search(query, language: nil, replacements: {}, subauth: nil, context: false, performance_data: false) # rubocop:disable Metrics/ParameterLists
5
+ saved_performance_data = performance_data
6
+ performance_data = true
7
+ full_results = super
8
+ QaServer::PerformanceHistory.save_result(dt_stamp: Time.now,
9
+ authority: authority_name,
10
+ action: 'search',
11
+ size_bytes: full_results[:performance][:fetched_bytes],
12
+ load_time_ms: (full_results[:performance][:fetch_time_s] * 1000),
13
+ normalization_time_ms: (full_results[:performance][:normalization_time_s] * 1000))
14
+ saved_performance_data ? full_results : full_results[:results]
15
+ end
16
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+ # This presenter class provides all data needed by the view that checks the status of authorities.
3
+
4
+ require 'json'
5
+
6
+ module QaServer
7
+ class FetchPresenter
8
+ # @param authorities_list [Array<String>] a list of all loaded authorities' names
9
+ # @param authority [String] name of the authority from which the term was fetched (e.g. 'LOCSUBJECT_LD4L_CACHE')
10
+ # @param uri [String] requested URI (e.g. 'http://id.loc.gov/authorities/subjects/sh2003008312')
11
+ # @param format [String] return results in this format (e.g. 'json', 'jsonld', 'n3')
12
+ # @param term_results [String] results of fetching a term in the requested format
13
+ def initialize(authorities_list:, authority: nil, uri: nil, format: nil, term_results: nil)
14
+ @authorities_list = authorities_list.map(&:upcase)
15
+ @authority = authority.present? ? authority.upcase : nil
16
+ @uri = uri
17
+ @format = format
18
+ @term_results = term_results
19
+ end
20
+
21
+ # @return [Array<String>] A list of all loaded authorities' names
22
+ # @example ['AGROVOC_DIRECT', 'AGROVOC_LD4L_CACHE', 'LOCNAMES_LD4L_CACHE']
23
+ attr_reader :authorities_list
24
+
25
+ # @return [String] Name of authority that was searched
26
+ # @example 'AGROVOC_LD4L_CACHE'
27
+ attr_reader :authority
28
+
29
+ # @return [String] The requested URI
30
+ # @example 'http://id.loc.gov/authorities/subjects/sh2003008312'
31
+ attr_reader :uri
32
+
33
+ # @return [Array<String>] list of supported formats
34
+ def formats_list
35
+ ['json', 'jsonld', 'n3']
36
+ end
37
+
38
+ # @return [String] format for the returned results of fetching the term
39
+ # @example 'json', 'jsonld', 'n3'
40
+ attr_reader :format
41
+
42
+ def json?
43
+ format.casecmp? 'json'
44
+ end
45
+
46
+ def jsonld?
47
+ format.casecmp? 'jsonld'
48
+ end
49
+
50
+ def n3?
51
+ format.casecmp? 'n3'
52
+ end
53
+
54
+ # @return [String] results for the term fetch in the requested format
55
+ def term_results
56
+ return JSON.pretty_generate(@term_results) if json?
57
+ @term_results
58
+ end
59
+
60
+ # @return [Boolean] true if search results exist; otherwise false
61
+ def term_results?
62
+ @term_results.present?
63
+ end
64
+ end
65
+ end
@@ -6,13 +6,27 @@ require 'gruff'
6
6
  # This presenter class provides all data needed by the view that monitors status of authorities.
7
7
  module QaServer
8
8
  class MonitorStatusPresenter # rubocop:disable Metrics/ClassLength
9
+ class_attribute :performance_history_class
10
+ self.performance_history_class = QaServer::PerformanceHistory
11
+
9
12
  HISTORICAL_AUTHORITY_NAME_IDX = 0
10
13
  HISTORICAL_FAILURE_COUNT_IDX = 1
11
14
  HISTORICAL_PASSING_COUNT_IDX = 2
12
15
 
16
+ PERFORMANCE_FOR_DAY_KEY = performance_history_class::PERFORMANCE_FOR_DAY_KEY
17
+ PERFORMANCE_BY_HOUR_KEY = performance_history_class::PERFORMANCE_BY_HOUR_KEY
18
+ PERFORMANCE_FOR_MONTH_KEY = performance_history_class::PERFORMANCE_FOR_MONTH_KEY
19
+ PERFORMANCE_BY_DAY_KEY = performance_history_class::PERFORMANCE_BY_DAY_KEY
20
+ PERFORMANCE_FOR_YEAR_KEY = performance_history_class::PERFORMANCE_FOR_YEAR_KEY
21
+ PERFORMANCE_BY_MONTH_KEY = performance_history_class::PERFORMANCE_BY_MONTH_KEY
22
+ LOAD_TIME_KEY = performance_history_class::LOAD_TIME_KEY
23
+ NORMALIZATION_TIME_KEY = performance_history_class::NORMALIZATION_TIME_KEY
24
+ COMBINED_TIME_KEY = performance_history_class::COMBINED_TIME_KEY
25
+
13
26
  # @param current_summary [ScenarioRunSummary] summary status of the latest run of test scenarios
14
27
  # @param current_data [Array<Hash>] current set of failures for the latest test run, if any
15
28
  # @param historical_summary_data [Array<Hash>] summary of past failuring runs per authority to drive chart
29
+ # @param performance_data [Hash<Hash>] performance data
16
30
  def initialize(current_summary:, current_failure_data:, historical_summary_data:, performance_data:)
17
31
  @current_summary = current_summary
18
32
  @current_failure_data = current_failure_data
@@ -101,14 +115,14 @@ module QaServer
101
115
  def historical_graph
102
116
  # g = Gruff::SideStackedBar.new('800x400')
103
117
  g = Gruff::SideStackedBar.new
104
- graph_theme(g)
118
+ historical_graph_theme(g)
105
119
  g.title = ''
106
120
  historical_data = rework_historical_data_for_gruff
107
121
  g.labels = historical_data[0]
108
122
  g.data('Fail', historical_data[1])
109
123
  g.data('Pass', historical_data[2])
110
124
  g.write historical_graph_full_path
111
- File.join(historical_graph_relative_path, historical_graph_filename)
125
+ File.join(graph_relative_path, historical_graph_filename)
112
126
  end
113
127
 
114
128
  # @return [String] the name of the css style class to use for the status cell based on the status of the scenario test.
@@ -175,23 +189,52 @@ module QaServer
175
189
  QaServer.config.display_historical_datatable?
176
190
  end
177
191
 
192
+ def performance_data?
193
+ @performance_data.present?
194
+ end
195
+
196
+ def performance_for_day_graph
197
+ performance_graph_file(rework_performance_data_for_gruff(@performance_data[PERFORMANCE_FOR_DAY_KEY], :hour),
198
+ performance_for_day_graph_full_path,
199
+ performance_for_day_graph_filename,
200
+ I18n.t('qa_server.monitor_status.performance.x_axis_hour'))
201
+ end
202
+
203
+ def performance_for_month_graph
204
+ performance_graph_file(rework_performance_data_for_gruff(@performance_data[PERFORMANCE_FOR_MONTH_KEY], :day),
205
+ performance_for_month_graph_full_path,
206
+ performance_for_month_graph_filename,
207
+ I18n.t('qa_server.monitor_status.performance.x_axis_day'))
208
+ end
209
+
210
+ def performance_for_year_graph
211
+ performance_graph_file(rework_performance_data_for_gruff(@performance_data[PERFORMANCE_FOR_YEAR_KEY], :month),
212
+ performance_for_year_graph_full_path,
213
+ performance_for_year_graph_filename,
214
+ I18n.t('qa_server.monitor_status.performance.x_axis_month'))
215
+ end
216
+
178
217
  private
179
218
 
180
- def graph_theme(g)
219
+ def historical_graph_theme(g)
181
220
  g.theme_pastel
182
221
  g.colors = ['#ffcccc', '#ccffcc']
183
222
  g.marker_font_size = 12
184
223
  g.x_axis_increment = 10
185
224
  end
186
225
 
187
- def historical_graph_full_path
188
- path = Rails.root.join('app', 'assets', 'images', historical_graph_relative_path)
226
+ def graph_relative_path
227
+ File.join('qa_server', 'charts')
228
+ end
229
+
230
+ def graph_full_path(graph_filename)
231
+ path = Rails.root.join('app', 'assets', 'images', graph_relative_path)
189
232
  FileUtils.mkdir_p path
190
- File.join(path, historical_graph_filename)
233
+ File.join(path, graph_filename)
191
234
  end
192
235
 
193
- def historical_graph_relative_path
194
- File.join('qa_server', 'charts')
236
+ def historical_graph_full_path
237
+ graph_full_path(historical_graph_filename)
195
238
  end
196
239
 
197
240
  def historical_graph_filename
@@ -211,5 +254,68 @@ module QaServer
211
254
  end
212
255
  [labels, fail_data, pass_data]
213
256
  end
257
+
258
+ def performance_graph_theme(g, x_axis_label)
259
+ g.theme_pastel
260
+ g.colors = ['#81adf4', '#8696b0', '#06578a']
261
+ g.marker_font_size = 12
262
+ g.x_axis_increment = 10
263
+ g.x_axis_label = x_axis_label
264
+ g.y_axis_label = I18n.t('qa_server.monitor_status.performance.y_axis_ms')
265
+ g.dot_radius = 3
266
+ g.line_width = 2
267
+ g.minimum_value = 0
268
+ g.maximum_value = 1000
269
+ end
270
+
271
+ def performance_for_day_graph_filename
272
+ 'performance_for_day_graph.png'
273
+ end
274
+
275
+ def performance_for_day_graph_full_path
276
+ graph_full_path(performance_for_day_graph_filename)
277
+ end
278
+
279
+ def performance_for_month_graph_filename
280
+ 'performance_for_month_graph.png'
281
+ end
282
+
283
+ def performance_for_month_graph_full_path
284
+ graph_full_path(performance_for_month_graph_filename)
285
+ end
286
+
287
+ def performance_for_year_graph_filename
288
+ 'performance_for_year_graph.png'
289
+ end
290
+
291
+ def performance_for_year_graph_full_path
292
+ graph_full_path(performance_for_year_graph_filename)
293
+ end
294
+
295
+ def rework_performance_data_for_gruff(performance_data, label_key)
296
+ labels = {}
297
+ load_data = []
298
+ normalization_data = []
299
+ combined_data = []
300
+ performance_data.each do |i, data|
301
+ labels[i] = data[label_key]
302
+ load_data << data[LOAD_TIME_KEY]
303
+ normalization_data << data[NORMALIZATION_TIME_KEY]
304
+ combined_data << data[COMBINED_TIME_KEY]
305
+ end
306
+ [labels, load_data, normalization_data, combined_data]
307
+ end
308
+
309
+ def performance_graph_file(performance_data, performance_graph_full_path, performance_graph_filename, x_axis_label)
310
+ g = Gruff::Line.new
311
+ performance_graph_theme(g, x_axis_label)
312
+ g.title = ''
313
+ g.labels = performance_data[0]
314
+ g.data(I18n.t('qa_server.monitor_status.performance.load_time_ms'), performance_data[1])
315
+ g.data(I18n.t('qa_server.monitor_status.performance.normalization_time_ms'), performance_data[2])
316
+ g.data(I18n.t('qa_server.monitor_status.performance.combined_time_ms'), performance_data[3])
317
+ g.write performance_graph_full_path
318
+ File.join(graph_relative_path, performance_graph_filename)
319
+ end
214
320
  end
215
321
  end