good_job 4.0.3 → 4.1.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 +71 -0
- data/README.md +1 -1
- data/app/charts/good_job/base_chart.rb +25 -0
- data/app/charts/good_job/performance_index_chart.rb +69 -0
- data/app/charts/good_job/performance_show_chart.rb +71 -0
- data/app/charts/good_job/scheduled_by_queue_chart.rb +23 -28
- data/app/controllers/good_job/application_controller.rb +1 -1
- data/app/controllers/good_job/frontends_controller.rb +6 -2
- data/app/controllers/good_job/metrics_controller.rb +5 -15
- data/app/controllers/good_job/performance_controller.rb +6 -1
- data/app/frontend/good_job/icons.svg +79 -0
- data/app/frontend/good_job/modules/charts.js +5 -17
- data/app/frontend/good_job/style.css +5 -0
- data/app/helpers/good_job/application_helper.rb +9 -1
- data/app/helpers/good_job/icons_helper.rb +8 -5
- data/app/models/concerns/good_job/advisory_lockable.rb +17 -7
- data/app/models/concerns/good_job/error_events.rb +14 -35
- data/app/models/concerns/good_job/reportable.rb +8 -12
- data/app/models/good_job/batch.rb +10 -5
- data/app/models/good_job/batch_record.rb +18 -15
- data/app/models/good_job/discrete_execution.rb +6 -60
- data/app/models/good_job/execution.rb +59 -4
- data/app/models/good_job/execution_result.rb +6 -6
- data/app/models/good_job/job.rb +569 -14
- data/app/models/good_job/process.rb +13 -29
- data/app/views/good_job/batches/_jobs.erb +1 -1
- data/app/views/good_job/batches/_table.erb +1 -1
- data/app/views/good_job/jobs/index.html.erb +1 -1
- data/app/views/good_job/performance/index.html.erb +3 -1
- data/app/views/good_job/performance/show.html.erb +5 -0
- data/app/views/good_job/shared/_filter.erb +2 -2
- data/app/views/layouts/good_job/application.html.erb +7 -7
- data/config/brakeman.ignore +75 -0
- data/config/locales/de.yml +52 -48
- data/config/locales/en.yml +4 -0
- data/config/locales/es.yml +16 -12
- data/config/locales/fr.yml +4 -0
- data/config/locales/it.yml +4 -0
- data/config/locales/ja.yml +4 -0
- data/config/locales/ko.yml +4 -0
- data/config/locales/nl.yml +4 -0
- data/config/locales/pt-BR.yml +4 -0
- data/config/locales/ru.yml +4 -0
- data/config/locales/tr.yml +4 -0
- data/config/locales/uk.yml +4 -0
- data/config/routes.rb +4 -4
- data/lib/good_job/active_job_extensions/concurrency.rb +105 -98
- data/lib/good_job/adapter/inline_buffer.rb +73 -0
- data/lib/good_job/adapter.rb +59 -53
- data/lib/good_job/capsule_tracker.rb +1 -1
- data/lib/good_job/configuration.rb +3 -4
- data/lib/good_job/cron_manager.rb +1 -3
- data/lib/good_job/current_thread.rb +4 -4
- data/lib/good_job/notifier.rb +7 -0
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +6 -5
- metadata +10 -20
- data/app/models/good_job/base_execution.rb +0 -609
- data/app/views/good_job/shared/icons/_arrow_clockwise.html.erb +0 -5
- data/app/views/good_job/shared/icons/_check.html.erb +0 -5
- data/app/views/good_job/shared/icons/_circle_half.html.erb +0 -4
- data/app/views/good_job/shared/icons/_clock.html.erb +0 -5
- data/app/views/good_job/shared/icons/_dash_circle.html.erb +0 -5
- data/app/views/good_job/shared/icons/_dots.html.erb +0 -3
- data/app/views/good_job/shared/icons/_eject.html.erb +0 -4
- data/app/views/good_job/shared/icons/_exclamation.html.erb +0 -5
- data/app/views/good_job/shared/icons/_globe.html.erb +0 -3
- data/app/views/good_job/shared/icons/_info.html.erb +0 -4
- data/app/views/good_job/shared/icons/_moon_stars_fill.html.erb +0 -5
- data/app/views/good_job/shared/icons/_pause.html.erb +0 -4
- data/app/views/good_job/shared/icons/_play.html.erb +0 -4
- data/app/views/good_job/shared/icons/_skip_forward.html.erb +0 -4
- data/app/views/good_job/shared/icons/_stop.html.erb +0 -4
- data/app/views/good_job/shared/icons/_sun_fill.html.erb +0 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 660e78528305c41b14c2926d9747037a091b57f952acd14246af99180991b434
|
|
4
|
+
data.tar.gz: 8bc407843b2d17fbeceb36507eb2e06177460f1bc4b8b4133560e1c4daa6ce3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8847a962731fec2c13683d3948e3ae51c0730bbcdd81853dfa3d6c2168bb05105ae2cf4c8f073a9c133d3f634473bdfaf58fefc08b07a0bbb8f51686e4b62ff0
|
|
7
|
+
data.tar.gz: c89b843399fe9bdc0d492c89ce1f481da62201689cc0f15ada8d73135308be1ee23295e54bae1fa586d1c9a50e2ca62a072784a4ac0fb9a5be3ee83ed374b1d7
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v4.1.1](https://github.com/bensheldon/good_job/tree/v4.1.1) (2024-07-31)
|
|
4
|
+
|
|
5
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v1.99.2...v4.1.1)
|
|
6
|
+
|
|
7
|
+
**Implemented enhancements:**
|
|
8
|
+
|
|
9
|
+
- Convert Concurrency extension to use transaction-level \(xact\) advisory locks [\#1439](https://github.com/bensheldon/good_job/pull/1439) ([bensheldon](https://github.com/bensheldon))
|
|
10
|
+
|
|
11
|
+
**Fixed bugs:**
|
|
12
|
+
|
|
13
|
+
- Fix N+1 on dashboard batches index page [\#1442](https://github.com/bensheldon/good_job/pull/1442) ([bensheldon](https://github.com/bensheldon))
|
|
14
|
+
- Remove duplicate word on batches show page [\#1441](https://github.com/bensheldon/good_job/pull/1441) ([Earlopain](https://github.com/Earlopain))
|
|
15
|
+
- Ensure remaining inline jobs are unlocked when one job raises in Adapter\#enqueue\_all [\#1438](https://github.com/bensheldon/good_job/pull/1438) ([bensheldon](https://github.com/bensheldon))
|
|
16
|
+
|
|
17
|
+
**Closed issues:**
|
|
18
|
+
|
|
19
|
+
- Using Good Job to track all ActiveJob executions, even those with `perform_now` [\#1448](https://github.com/bensheldon/good_job/issues/1448)
|
|
20
|
+
- RuntimeError when running good\_job executable after updating v3 -\> v4 [\#1445](https://github.com/bensheldon/good_job/issues/1445)
|
|
21
|
+
- 2.99 -\> 3.0.2 migration not applying cleanly from a fresh DB [\#1435](https://github.com/bensheldon/good_job/issues/1435)
|
|
22
|
+
- \[Enhance\] Enhance performance via counter cache [\#1375](https://github.com/bensheldon/good_job/issues/1375)
|
|
23
|
+
- Change how svg images are inserted into partials [\#1364](https://github.com/bensheldon/good_job/issues/1364)
|
|
24
|
+
|
|
25
|
+
**Merged pull requests:**
|
|
26
|
+
|
|
27
|
+
- Improve some Spanish transcriptions [\#1452](https://github.com/bensheldon/good_job/pull/1452) ([sebastian-cloudnonic](https://github.com/sebastian-cloudnonic))
|
|
28
|
+
- All running jobs now have `performed_at` set so use that in status query; fix flaky test that took advisory lock in `before` block [\#1444](https://github.com/bensheldon/good_job/pull/1444) ([bensheldon](https://github.com/bensheldon))
|
|
29
|
+
- Handle empty asset format in Frontends controller [\#1443](https://github.com/bensheldon/good_job/pull/1443) ([bensheldon](https://github.com/bensheldon))
|
|
30
|
+
- Update development dependencies, Ruby 3.3.4 [\#1437](https://github.com/bensheldon/good_job/pull/1437) ([bensheldon](https://github.com/bensheldon))
|
|
31
|
+
- Refactor inline adapter to enable deferred execution after enqueue to allow batch-callbacks to use transaction-based advisory lock [\#1433](https://github.com/bensheldon/good_job/pull/1433) ([bensheldon](https://github.com/bensheldon))
|
|
32
|
+
- German translation pass [\#1432](https://github.com/bensheldon/good_job/pull/1432) ([Earlopain](https://github.com/Earlopain))
|
|
33
|
+
- Add Brakeman to linters [\#1431](https://github.com/bensheldon/good_job/pull/1431) ([bensheldon](https://github.com/bensheldon))
|
|
34
|
+
- Remove references to and ignore `good_jobs.retried_good_job_id` column [\#1430](https://github.com/bensheldon/good_job/pull/1430) ([bensheldon](https://github.com/bensheldon))
|
|
35
|
+
- Refactor Concurrency extension for Rails 6.1+ compatibility [\#1429](https://github.com/bensheldon/good_job/pull/1429) ([bensheldon](https://github.com/bensheldon))
|
|
36
|
+
- Use svg `use` for svg icons [\#1428](https://github.com/bensheldon/good_job/pull/1428) ([Earlopain](https://github.com/Earlopain))
|
|
37
|
+
- Replace references to "Discrete" executions with simply Executions; deprecate `GoodJob::DiscreteExecution` [\#1427](https://github.com/bensheldon/good_job/pull/1427) ([bensheldon](https://github.com/bensheldon))
|
|
38
|
+
- Refactor Adapter to reference jobs, not executions [\#1426](https://github.com/bensheldon/good_job/pull/1426) ([bensheldon](https://github.com/bensheldon))
|
|
39
|
+
|
|
40
|
+
## [v1.99.2](https://github.com/bensheldon/good_job/tree/v1.99.2) (2024-07-18)
|
|
41
|
+
|
|
42
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.1.0...v1.99.2)
|
|
43
|
+
|
|
44
|
+
## [v4.1.0](https://github.com/bensheldon/good_job/tree/v4.1.0) (2024-07-16)
|
|
45
|
+
|
|
46
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v3.99.1...v4.1.0)
|
|
47
|
+
|
|
48
|
+
**Implemented enhancements:**
|
|
49
|
+
|
|
50
|
+
- Add keepalive SQL query to Notifier [\#1423](https://github.com/bensheldon/good_job/pull/1423) ([bensheldon](https://github.com/bensheldon))
|
|
51
|
+
- Latency charts and histograms for individual job classes [\#1411](https://github.com/bensheldon/good_job/pull/1411) ([Earlopain](https://github.com/Earlopain))
|
|
52
|
+
|
|
53
|
+
**Fixed bugs:**
|
|
54
|
+
|
|
55
|
+
- Fix nonexistant association error between DiscreteExecution and Execution [\#1425](https://github.com/bensheldon/good_job/pull/1425) ([bensheldon](https://github.com/bensheldon))
|
|
56
|
+
|
|
57
|
+
**Closed issues:**
|
|
58
|
+
|
|
59
|
+
- Could not find the inverse association for execution \(:discrete\_executions in GoodJob::Execution\) [\#1424](https://github.com/bensheldon/good_job/issues/1424)
|
|
60
|
+
- 3.99.1 is marked as the latest version, not 4.0.3 [\#1422](https://github.com/bensheldon/good_job/issues/1422)
|
|
61
|
+
- How to maximise amount of jobs executed in parallel [\#1418](https://github.com/bensheldon/good_job/issues/1418)
|
|
62
|
+
- Performance Metrics for individual jobs [\#1397](https://github.com/bensheldon/good_job/issues/1397)
|
|
63
|
+
|
|
64
|
+
**Merged pull requests:**
|
|
65
|
+
|
|
66
|
+
- Remove some now unnecessary checks against `locked_by_id` existence [\#1421](https://github.com/bensheldon/good_job/pull/1421) ([Earlopain](https://github.com/Earlopain))
|
|
67
|
+
- Use rails enum for `error_event` and `lock_type` [\#1420](https://github.com/bensheldon/good_job/pull/1420) ([Earlopain](https://github.com/Earlopain))
|
|
68
|
+
- Add a little more wording to the v4 "ready to upgrade" instructions [\#1415](https://github.com/bensheldon/good_job/pull/1415) ([bensheldon](https://github.com/bensheldon))
|
|
69
|
+
|
|
70
|
+
## [v3.99.1](https://github.com/bensheldon/good_job/tree/v3.99.1) (2024-07-10)
|
|
71
|
+
|
|
72
|
+
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.0.3...v3.99.1)
|
|
73
|
+
|
|
3
74
|
## [v4.0.3](https://github.com/bensheldon/good_job/tree/v4.0.3) (2024-07-10)
|
|
4
75
|
|
|
5
76
|
[Full Changelog](https://github.com/bensheldon/good_job/compare/v4.0.2...v4.0.3)
|
data/README.md
CHANGED
|
@@ -876,7 +876,7 @@ To perform upgrades to the GoodJob database tables:
|
|
|
876
876
|
|
|
877
877
|
#### Upgrading v3 to v4
|
|
878
878
|
|
|
879
|
-
GoodJob v4 changes how job and job execution records are stored in the database; moving from job and executions being commingled in the `good_jobs` table to separately and discretely storing job executions in `good_job_executions`. To safely upgrade, all unfinished jobs must use the new format. This change was introduced in GoodJob [v3.15.4 (April 2023)](https://github.com/bensheldon/good_job/releases/tag/v3.15.4), so your application is likely ready-to-upgrade
|
|
879
|
+
GoodJob v4 changes how job and job execution records are stored in the database; moving from job and executions being commingled in the `good_jobs` table to separately and discretely storing job executions in `good_job_executions`. To safely upgrade, all unfinished jobs must use the new format. This change was introduced in GoodJob [v3.15.4 (April 2023)](https://github.com/bensheldon/good_job/releases/tag/v3.15.4), so your application is likely ready-to-upgrade in this respect if you have kept up with GoodJob updates and applied migrations (`bin/rails g good_job:update`). _Please be sure to doublecheck you are not missing subsequent migrations or deprecations too by following the instructions below._
|
|
880
880
|
|
|
881
881
|
To upgrade:
|
|
882
882
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GoodJob
|
|
4
|
+
class BaseChart
|
|
5
|
+
def start_end_binds
|
|
6
|
+
end_time = Time.current
|
|
7
|
+
start_time = end_time - 1.day
|
|
8
|
+
|
|
9
|
+
[
|
|
10
|
+
ActiveRecord::Relation::QueryAttribute.new('start_time', start_time, ActiveRecord::Type::DateTime.new),
|
|
11
|
+
ActiveRecord::Relation::QueryAttribute.new('end_time', end_time, ActiveRecord::Type::DateTime.new),
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def string_to_hsl(string)
|
|
16
|
+
hash_value = string.sum
|
|
17
|
+
|
|
18
|
+
hue = hash_value % 360
|
|
19
|
+
saturation = (hash_value % 50) + 50
|
|
20
|
+
lightness = '50'
|
|
21
|
+
|
|
22
|
+
"hsl(#{hue}, #{saturation}%, #{lightness}%)"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GoodJob
|
|
4
|
+
class PerformanceIndexChart < BaseChart
|
|
5
|
+
def data
|
|
6
|
+
table_name = GoodJob::Execution.table_name
|
|
7
|
+
|
|
8
|
+
sum_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish))
|
|
9
|
+
SELECT *
|
|
10
|
+
FROM generate_series(
|
|
11
|
+
date_trunc('hour', $1::timestamp),
|
|
12
|
+
date_trunc('hour', $2::timestamp),
|
|
13
|
+
'1 hour'
|
|
14
|
+
) timestamp
|
|
15
|
+
LEFT JOIN (
|
|
16
|
+
SELECT
|
|
17
|
+
date_trunc('hour', scheduled_at) AS scheduled_at,
|
|
18
|
+
job_class,
|
|
19
|
+
SUM(duration) AS sum
|
|
20
|
+
FROM #{table_name} sources
|
|
21
|
+
GROUP BY date_trunc('hour', scheduled_at), job_class
|
|
22
|
+
) sources ON sources.scheduled_at = timestamp
|
|
23
|
+
ORDER BY timestamp ASC
|
|
24
|
+
SQL
|
|
25
|
+
|
|
26
|
+
executions_data = GoodJob::Job.connection.exec_query(GoodJob::Job.pg_or_jdbc_query(sum_query), "GoodJob Performance Chart", start_end_binds)
|
|
27
|
+
|
|
28
|
+
job_names = executions_data.reject { |d| d['sum'].nil? }.map { |d| d['job_class'] || BaseFilter::EMPTY }.uniq
|
|
29
|
+
labels = []
|
|
30
|
+
jobs_data = executions_data.to_a.group_by { |d| d['timestamp'] }.each_with_object({}) do |(timestamp, values), hash|
|
|
31
|
+
labels << timestamp.in_time_zone.strftime('%H:%M')
|
|
32
|
+
job_names.each do |job_class|
|
|
33
|
+
sum = values.find { |d| d['job_class'] == job_class }&.[]('sum')
|
|
34
|
+
duration = sum ? ActiveSupport::Duration.parse(sum).to_f : 0
|
|
35
|
+
(hash[job_class] ||= []) << duration
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
{
|
|
40
|
+
type: "line",
|
|
41
|
+
data: {
|
|
42
|
+
labels: labels,
|
|
43
|
+
datasets: jobs_data.map do |job_class, data|
|
|
44
|
+
label = job_class || '(none)'
|
|
45
|
+
{
|
|
46
|
+
label: label,
|
|
47
|
+
data: data,
|
|
48
|
+
backgroundColor: string_to_hsl(label),
|
|
49
|
+
borderColor: string_to_hsl(label),
|
|
50
|
+
}
|
|
51
|
+
end,
|
|
52
|
+
},
|
|
53
|
+
options: {
|
|
54
|
+
plugins: {
|
|
55
|
+
title: {
|
|
56
|
+
display: true,
|
|
57
|
+
text: I18n.t("good_job.performance.index.chart_title"),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
scales: {
|
|
61
|
+
y: {
|
|
62
|
+
beginAtZero: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GoodJob
|
|
4
|
+
class PerformanceShowChart < BaseChart
|
|
5
|
+
# These numbers are lifted from Sidekiq
|
|
6
|
+
BUCKET_INTERVALS = [
|
|
7
|
+
0.02, 0.03, 0.045, 0.065, 0.1,
|
|
8
|
+
0.15, 0.225, 0.335, 0.5, 0.75,
|
|
9
|
+
1.1, 1.7, 2.5, 3.8, 5.75,
|
|
10
|
+
8.5, 13, 20, 30, 45,
|
|
11
|
+
65, 100, 150, 225, 335,
|
|
12
|
+
10**8 # About 3 years
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
def initialize(job_class)
|
|
16
|
+
super()
|
|
17
|
+
@job_class = job_class
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def data
|
|
21
|
+
table_name = GoodJob::Execution.table_name
|
|
22
|
+
|
|
23
|
+
interval_entries = BUCKET_INTERVALS.map { "interval '#{_1}'" }.join(",")
|
|
24
|
+
sum_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish))
|
|
25
|
+
SELECT
|
|
26
|
+
WIDTH_BUCKET(duration, ARRAY[#{interval_entries}]) as bucket_index,
|
|
27
|
+
COUNT(WIDTH_BUCKET(duration, ARRAY[#{interval_entries}])) AS count
|
|
28
|
+
FROM #{table_name} sources
|
|
29
|
+
WHERE
|
|
30
|
+
scheduled_at > $1::timestamp
|
|
31
|
+
AND scheduled_at < $2::timestamp
|
|
32
|
+
AND job_class = $3
|
|
33
|
+
AND duration IS NOT NULL
|
|
34
|
+
GROUP BY bucket_index
|
|
35
|
+
SQL
|
|
36
|
+
|
|
37
|
+
binds = [
|
|
38
|
+
*start_end_binds,
|
|
39
|
+
@job_class,
|
|
40
|
+
]
|
|
41
|
+
labels = BUCKET_INTERVALS.map { |interval| GoodJob::ApplicationController.helpers.format_duration(interval) }
|
|
42
|
+
labels[-1] = I18n.t("good_job.performance.show.slow")
|
|
43
|
+
executions_data = GoodJob::Job.connection.exec_query(GoodJob::Job.pg_or_jdbc_query(sum_query), "GoodJob Performance Job Chart", binds)
|
|
44
|
+
executions_data = executions_data.to_a.index_by { |data| data["bucket_index"] }
|
|
45
|
+
|
|
46
|
+
bucket_data = 0.upto(BUCKET_INTERVALS.count).map do |bucket_index|
|
|
47
|
+
executions_data.dig(bucket_index, "count") || 0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
type: "bar",
|
|
52
|
+
data: {
|
|
53
|
+
labels: labels,
|
|
54
|
+
datasets: [{
|
|
55
|
+
label: @job_class,
|
|
56
|
+
data: bucket_data,
|
|
57
|
+
backgroundColor: string_to_hsl(@job_class),
|
|
58
|
+
borderColor: string_to_hsl(@job_class),
|
|
59
|
+
}],
|
|
60
|
+
},
|
|
61
|
+
options: {
|
|
62
|
+
scales: {
|
|
63
|
+
y: {
|
|
64
|
+
beginAtZero: true,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module GoodJob
|
|
4
|
-
class ScheduledByQueueChart
|
|
4
|
+
class ScheduledByQueueChart < BaseChart
|
|
5
5
|
def initialize(filter)
|
|
6
|
+
super()
|
|
6
7
|
@filter = filter
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
def data
|
|
10
|
-
end_time = Time.current
|
|
11
|
-
start_time = end_time - 1.day
|
|
12
11
|
table_name = GoodJob::Job.table_name
|
|
13
12
|
|
|
14
13
|
count_query = Arel.sql(GoodJob::Job.pg_or_jdbc_query(<<~SQL.squish))
|
|
@@ -31,11 +30,7 @@ module GoodJob
|
|
|
31
30
|
ORDER BY timestamp ASC
|
|
32
31
|
SQL
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
ActiveRecord::Relation::QueryAttribute.new('start_time', start_time, ActiveRecord::Type::DateTime.new),
|
|
36
|
-
ActiveRecord::Relation::QueryAttribute.new('end_time', end_time, ActiveRecord::Type::DateTime.new),
|
|
37
|
-
]
|
|
38
|
-
executions_data = GoodJob::Job.connection.exec_query(GoodJob::Job.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", binds)
|
|
33
|
+
executions_data = GoodJob::Job.connection.exec_query(GoodJob::Job.pg_or_jdbc_query(count_query), "GoodJob Dashboard Chart", start_end_binds)
|
|
39
34
|
|
|
40
35
|
queue_names = executions_data.reject { |d| d['count'].nil? }.map { |d| d['queue_name'] || BaseFilter::EMPTY }.uniq
|
|
41
36
|
labels = []
|
|
@@ -47,27 +42,27 @@ module GoodJob
|
|
|
47
42
|
end
|
|
48
43
|
|
|
49
44
|
{
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
label
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
45
|
+
type: "line",
|
|
46
|
+
data: {
|
|
47
|
+
labels: labels,
|
|
48
|
+
datasets: queues_data.map do |queue, data|
|
|
49
|
+
label = queue || '(none)'
|
|
50
|
+
{
|
|
51
|
+
label: label,
|
|
52
|
+
data: data,
|
|
53
|
+
backgroundColor: string_to_hsl(label),
|
|
54
|
+
borderColor: string_to_hsl(label),
|
|
55
|
+
}
|
|
56
|
+
end,
|
|
57
|
+
},
|
|
58
|
+
options: {
|
|
59
|
+
scales: {
|
|
60
|
+
y: {
|
|
61
|
+
beginAtZero: true,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
60
65
|
}
|
|
61
66
|
end
|
|
62
|
-
|
|
63
|
-
def string_to_hsl(string)
|
|
64
|
-
hash_value = string.sum
|
|
65
|
-
|
|
66
|
-
hue = hash_value % 360
|
|
67
|
-
saturation = (hash_value % 50) + 50
|
|
68
|
-
lightness = '50'
|
|
69
|
-
|
|
70
|
-
"hsl(#{hue}, #{saturation}%, #{lightness}%)"
|
|
71
|
-
end
|
|
72
67
|
end
|
|
73
68
|
end
|
|
@@ -7,7 +7,7 @@ module GoodJob
|
|
|
7
7
|
around_action :use_good_job_locale
|
|
8
8
|
|
|
9
9
|
content_security_policy do |policy|
|
|
10
|
-
policy.default_src(:
|
|
10
|
+
policy.default_src(:self) if policy.default_src(*policy.default_src).blank?
|
|
11
11
|
policy.connect_src(:self) if policy.connect_src(*policy.connect_src).blank?
|
|
12
12
|
policy.base_uri(:none) if policy.base_uri(*policy.base_uri).blank?
|
|
13
13
|
policy.font_src(:self) if policy.font_src(*policy.font_src).blank?
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module GoodJob
|
|
4
4
|
class FrontendsController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
|
5
|
+
protect_from_forgery with: :exception
|
|
5
6
|
skip_after_action :verify_same_origin_request, raise: false
|
|
6
7
|
|
|
7
8
|
STATIC_ASSETS = {
|
|
@@ -15,6 +16,9 @@ module GoodJob
|
|
|
15
16
|
es_module_shims: GoodJob::Engine.root.join("app", "frontend", "good_job", "vendor", "es_module_shims.js"),
|
|
16
17
|
rails_ujs: GoodJob::Engine.root.join("app", "frontend", "good_job", "vendor", "rails_ujs.js"),
|
|
17
18
|
},
|
|
19
|
+
svg: {
|
|
20
|
+
icons: GoodJob::Engine.root.join("app", "frontend", "good_job", "icons.svg"),
|
|
21
|
+
},
|
|
18
22
|
}.freeze
|
|
19
23
|
|
|
20
24
|
MODULE_OVERRIDES = {
|
|
@@ -34,13 +38,13 @@ module GoodJob
|
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def static
|
|
37
|
-
render file: STATIC_ASSETS.dig(params[:format]
|
|
41
|
+
render file: STATIC_ASSETS.dig(params[:format]&.to_sym, params[:id]&.to_sym) || raise(ActionController::RoutingError, 'Not Found')
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
def module
|
|
41
45
|
raise(ActionController::RoutingError, 'Not Found') if params[:format] != "js"
|
|
42
46
|
|
|
43
|
-
render file: self.class.js_modules[params[:
|
|
47
|
+
render file: self.class.js_modules[params[:id]&.to_sym] || raise(ActionController::RoutingError, 'Not Found')
|
|
44
48
|
end
|
|
45
49
|
end
|
|
46
50
|
end
|
|
@@ -9,27 +9,17 @@ module GoodJob
|
|
|
9
9
|
processes_count = GoodJob::Process.active.count
|
|
10
10
|
|
|
11
11
|
render json: {
|
|
12
|
-
jobs_count: number_to_human(jobs_count),
|
|
13
|
-
batches_count: number_to_human(batches_count),
|
|
14
|
-
cron_entries_count: number_to_human(cron_entries_count),
|
|
15
|
-
processes_count: number_to_human(processes_count),
|
|
12
|
+
jobs_count: helpers.number_to_human(jobs_count),
|
|
13
|
+
batches_count: helpers.number_to_human(batches_count),
|
|
14
|
+
cron_entries_count: helpers.number_to_human(cron_entries_count),
|
|
15
|
+
processes_count: helpers.number_to_human(processes_count),
|
|
16
16
|
}
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def job_status
|
|
20
20
|
@filter = JobsFilter.new(params)
|
|
21
21
|
|
|
22
|
-
render json: @filter.states.transform_values { |count| number_with_delimiter(count) }
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
def number_to_human(count)
|
|
28
|
-
helpers.number_to_human(count, **helpers.translate_hash("good_job.number.human.decimal_units"))
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def number_with_delimiter(count)
|
|
32
|
-
helpers.number_with_delimiter(count, **helpers.translate_hash('good_job.number.format'))
|
|
22
|
+
render json: @filter.states.transform_values { |count| helpers.number_with_delimiter(count) }
|
|
33
23
|
end
|
|
34
24
|
end
|
|
35
25
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module GoodJob
|
|
4
4
|
class PerformanceController < ApplicationController
|
|
5
5
|
def index
|
|
6
|
-
@performances = GoodJob::
|
|
6
|
+
@performances = GoodJob::Execution
|
|
7
7
|
.where.not(job_class: nil)
|
|
8
8
|
.group(:job_class)
|
|
9
9
|
.select("
|
|
@@ -15,5 +15,10 @@ module GoodJob
|
|
|
15
15
|
")
|
|
16
16
|
.order("job_class")
|
|
17
17
|
end
|
|
18
|
+
|
|
19
|
+
def show
|
|
20
|
+
representative_job = GoodJob::Job.find_by!(job_class: params[:id])
|
|
21
|
+
@job_class = representative_job.job_class
|
|
22
|
+
end
|
|
18
23
|
end
|
|
19
24
|
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
3
|
+
<!-- https://icons.getbootstrap.com/icons/arrow-clockwise/ -->
|
|
4
|
+
<symbol id="arrow_clockwise" viewBox="0 0 16 16">
|
|
5
|
+
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z" />
|
|
6
|
+
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z" />
|
|
7
|
+
</symbol>
|
|
8
|
+
<!-- https://icons.getbootstrap.com/icons/check-circle/ -->
|
|
9
|
+
<symbol id="check" viewBox="0 0 16 16">
|
|
10
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
11
|
+
<path d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" />
|
|
12
|
+
</symbol>
|
|
13
|
+
<!-- https://icons.getbootstrap.com/icons/circle-half/ -->
|
|
14
|
+
<symbol id="circle_half" viewBox="0 0 16 16">
|
|
15
|
+
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
|
|
16
|
+
</symbol>
|
|
17
|
+
<!-- https://icons.getbootstrap.com/icons/clock/ -->
|
|
18
|
+
<symbol id="clock" viewBox="0 0 16 16">
|
|
19
|
+
<path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z" />
|
|
20
|
+
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z" />
|
|
21
|
+
</symbol>
|
|
22
|
+
<!-- https://icons.getbootstrap.com/icons/dash-circle/ -->
|
|
23
|
+
<symbol id="dash_circle" viewBox="0 0 16 16">
|
|
24
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
25
|
+
<path d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z" />
|
|
26
|
+
</symbol>
|
|
27
|
+
<!-- https://icons.getbootstrap.com/icons/three-dots/ -->
|
|
28
|
+
<symbol id="dots" viewBox="0 0 16 16">
|
|
29
|
+
<path d="M3 9.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z" />
|
|
30
|
+
</symbol>
|
|
31
|
+
<!-- https://icons.getbootstrap.com/icons/eject/ -->
|
|
32
|
+
<symbol id="eject" viewBox="0 0 16 16">
|
|
33
|
+
<path d="M7.27 1.047a1 1 0 0 1 1.46 0l6.345 6.77c.6.638.146 1.683-.73 1.683H1.656C.78 9.5.326 8.455.926 7.816L7.27 1.047zM14.346 8.5 8 1.731 1.654 8.5h12.692zM.5 11.5a1 1 0 0 1 1-1h13a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1h-13a1 1 0 0 1-1-1v-1zm14 0h-13v1h13v-1z" />
|
|
34
|
+
</symbol>
|
|
35
|
+
<!-- https://icons.getbootstrap.com/icons/exclamation-circle/ -->
|
|
36
|
+
<symbol id="exclamation" viewBox="0 0 16 16">
|
|
37
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
38
|
+
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z" />
|
|
39
|
+
</symbol>
|
|
40
|
+
<!-- https://icons.getbootstrap.com/icons/globe/ -->
|
|
41
|
+
<symbol id="globe" viewBox="0 0 16 16">
|
|
42
|
+
<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855A8 8 0 0 0 5.145 4H7.5zM4.09 4a9.3 9.3 0 0 1 .64-1.539 7 7 0 0 1 .597-.933A7.03 7.03 0 0 0 2.255 4zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a7 7 0 0 0-.656 2.5zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5zM8.5 5v2.5h2.99a12.5 12.5 0 0 0-.337-2.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5zM5.145 12q.208.58.468 1.068c.552 1.035 1.218 1.65 1.887 1.855V12zm.182 2.472a7 7 0 0 1-.597-.933A9.3 9.3 0 0 1 4.09 12H2.255a7 7 0 0 0 3.072 2.472M3.82 11a13.7 13.7 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5zm6.853 3.472A7 7 0 0 0 13.745 12H11.91a9.3 9.3 0 0 1-.64 1.539 7 7 0 0 1-.597.933M8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855q.26-.487.468-1.068zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.7 13.7 0 0 1-.312 2.5m2.802-3.5a7 7 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7 7 0 0 0-3.072-2.472c.218.284.418.598.597.933M10.855 4a8 8 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4z" />
|
|
43
|
+
</symbol>
|
|
44
|
+
<!-- https://icons.getbootstrap.com/icons/info-circle/ -->
|
|
45
|
+
<symbol id="info" viewBox="0 0 16 16">
|
|
46
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
|
47
|
+
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" />
|
|
48
|
+
</symbol>
|
|
49
|
+
<!-- https://icons.getbootstrap.com/icons/moon-stars-fill/ -->
|
|
50
|
+
<symbol id="moon_stars_fill" viewBox="0 0 16 16">
|
|
51
|
+
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
|
|
52
|
+
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
|
|
53
|
+
</symbol>
|
|
54
|
+
<!-- https://icons.getbootstrap.com/icons/pause-btn/ -->
|
|
55
|
+
<symbol id="pause" viewBox="0 0 16 16">
|
|
56
|
+
<path d="M6 3.5a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5zm4 0a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5z" />
|
|
57
|
+
</symbol>
|
|
58
|
+
<!-- https://icons.getbootstrap.com/icons/play/ -->
|
|
59
|
+
<symbol id="play" viewBox="0 0 16 16">
|
|
60
|
+
<path d="M10.804 8 5 4.633v6.734L10.804 8zm.792-.696a.802.802 0 0 1 0 1.392l-6.363 3.692C4.713 12.69 4 12.345 4 11.692V4.308c0-.653.713-.998 1.233-.696l6.363 3.692z" />
|
|
61
|
+
</symbol>
|
|
62
|
+
<!-- https://icons.getbootstrap.com/icons/skip-forward/ -->
|
|
63
|
+
<symbol id="skip_forward" viewBox="0 0 16 16">
|
|
64
|
+
<path d="M15.5 3.5a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-1 0V8.752l-6.267 3.636c-.52.302-1.233-.043-1.233-.696v-2.94l-6.267 3.636C.713 12.69 0 12.345 0 11.692V4.308c0-.653.713-.998 1.233-.696L7.5 7.248v-2.94c0-.653.713-.998 1.233-.696L15 7.248V4a.5.5 0 0 1 .5-.5zM1 4.633v6.734L6.804 8 1 4.633zm7.5 0v6.734L14.304 8 8.5 4.633z" />
|
|
65
|
+
</symbol>
|
|
66
|
+
<!-- https://icons.getbootstrap.com/icons/stop/ -->
|
|
67
|
+
<symbol id="stop" viewBox="0 0 16 16">
|
|
68
|
+
<path d="M3.5 5A1.5 1.5 0 0 1 5 3.5h6A1.5 1.5 0 0 1 12.5 5v6a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 11V5zM5 4.5a.5.5 0 0 0-.5.5v6a.5.5 0 0 0 .5.5h6a.5.5 0 0 0 .5-.5V5a.5.5 0 0 0-.5-.5H5z" />
|
|
69
|
+
</symbol>
|
|
70
|
+
<!-- https://icons.getbootstrap.com/icons/sun-fill/ -->
|
|
71
|
+
<symbol id="sun_fill" viewBox="0 0 16 16">
|
|
72
|
+
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
|
|
73
|
+
</symbol>
|
|
74
|
+
<!-- https://icons.getbootstrap.com/icons/trash/ -->
|
|
75
|
+
<symbol id="trash" viewBox="0 0 16 16">
|
|
76
|
+
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" />
|
|
77
|
+
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z" />
|
|
78
|
+
</symbol>
|
|
79
|
+
</svg>
|
|
@@ -4,25 +4,13 @@ function renderCharts(animate) {
|
|
|
4
4
|
for (let i = 0; i < charts.length; i++) {
|
|
5
5
|
const chartEl = charts[i];
|
|
6
6
|
const chartData = JSON.parse(chartEl.dataset.json);
|
|
7
|
+
chartData.options ||= {};
|
|
8
|
+
chartData.options.animation = animate;
|
|
9
|
+
chartData.options.responsive = true;
|
|
10
|
+
chartData.options.maintainAspectRatio = false;
|
|
7
11
|
|
|
8
12
|
const ctx = chartEl.getContext('2d');
|
|
9
|
-
const chart = new Chart(ctx,
|
|
10
|
-
type: 'line',
|
|
11
|
-
data: {
|
|
12
|
-
labels: chartData.labels,
|
|
13
|
-
datasets: chartData.datasets
|
|
14
|
-
},
|
|
15
|
-
options: {
|
|
16
|
-
animation: animate,
|
|
17
|
-
responsive: true,
|
|
18
|
-
maintainAspectRatio: false,
|
|
19
|
-
scales: {
|
|
20
|
-
y: {
|
|
21
|
-
beginAtZero: true
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
});
|
|
13
|
+
const chart = new Chart(ctx, chartData);
|
|
26
14
|
}
|
|
27
15
|
}
|
|
28
16
|
|
|
@@ -14,7 +14,7 @@ module GoodJob
|
|
|
14
14
|
if sec < 1
|
|
15
15
|
t 'good_job.duration.milliseconds', ms: (sec * 1000).floor
|
|
16
16
|
elsif sec < 10
|
|
17
|
-
t 'good_job.duration.less_than_10_seconds', sec: sec.floor
|
|
17
|
+
t 'good_job.duration.less_than_10_seconds', sec: number_with_delimiter(sec.floor(1))
|
|
18
18
|
elsif sec < 60
|
|
19
19
|
t 'good_job.duration.seconds', sec: sec.floor
|
|
20
20
|
elsif sec < 3600
|
|
@@ -30,6 +30,14 @@ module GoodJob
|
|
|
30
30
|
tag.time(text, datetime: timestamp, title: timestamp)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
def number_to_human(count)
|
|
34
|
+
super(count, **translate_hash("good_job.number.human.decimal_units"))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def number_with_delimiter(count)
|
|
38
|
+
super(count, **translate_hash('good_job.number.format'))
|
|
39
|
+
end
|
|
40
|
+
|
|
33
41
|
def translate_hash(key, **options)
|
|
34
42
|
translation_exists?(key, **options) ? translate(key, **options) : {}
|
|
35
43
|
end
|
|
@@ -31,11 +31,14 @@ module GoodJob
|
|
|
31
31
|
content_tag :span, icon, **options
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def render_icon(name, **options)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
def render_icon(name, class: nil, **options)
|
|
35
|
+
tag.svg(viewBox: "0 0 16 16", class: "svg-icon #{binding.local_variable_get(:class)}", **options) do
|
|
36
|
+
tag.use(fill: "currentColor", href: "#{icons_path}##{name}")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def icons_path
|
|
41
|
+
@_icons_path ||= frontend_static_path(:icons, format: :svg, locale: nil)
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
end
|