qa_server 2.1.0 → 2.2.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.
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