solid_queue_monitor 1.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -4
- data/app/assets/javascripts/solid_queue_monitor/application.js +393 -0
- data/app/{services/solid_queue_monitor/stylesheet_generator.rb → assets/stylesheets/solid_queue_monitor/application.css} +23 -12
- data/app/controllers/solid_queue_monitor/application_controller.rb +2 -2
- data/app/controllers/solid_queue_monitor/assets_controller.rb +52 -0
- data/app/controllers/solid_queue_monitor/base_controller.rb +0 -29
- data/app/controllers/solid_queue_monitor/failed_jobs_controller.rb +3 -7
- data/app/controllers/solid_queue_monitor/in_progress_jobs_controller.rb +3 -6
- data/app/controllers/solid_queue_monitor/jobs_controller.rb +3 -7
- data/app/controllers/solid_queue_monitor/overview_controller.rb +3 -12
- data/app/controllers/solid_queue_monitor/queues_controller.rb +4 -18
- data/app/controllers/solid_queue_monitor/ready_jobs_controller.rb +3 -6
- data/app/controllers/solid_queue_monitor/recurring_jobs_controller.rb +3 -6
- data/app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb +3 -7
- data/app/controllers/solid_queue_monitor/search_controller.rb +3 -4
- data/app/controllers/solid_queue_monitor/workers_controller.rb +24 -8
- data/app/helpers/solid_queue_monitor/application_helper.rb +46 -0
- data/app/helpers/solid_queue_monitor/chart_helper.rb +293 -0
- data/app/helpers/solid_queue_monitor/job_details_helper.rb +66 -0
- data/app/helpers/solid_queue_monitor/jobs_helper.rb +134 -0
- data/app/helpers/solid_queue_monitor/pagination_helper.rb +23 -0
- data/app/helpers/solid_queue_monitor/sort_helper.rb +30 -0
- data/app/helpers/solid_queue_monitor/workers_helper.rb +88 -0
- data/app/services/solid_queue_monitor/asset_cache.rb +56 -0
- data/app/views/layouts/solid_queue_monitor/application.html.erb +25 -0
- data/app/views/solid_queue_monitor/failed_jobs/_row.html.erb +26 -0
- data/app/views/solid_queue_monitor/failed_jobs/index.html.erb +38 -0
- data/app/views/solid_queue_monitor/in_progress_jobs/_row.html.erb +13 -0
- data/app/views/solid_queue_monitor/in_progress_jobs/index.html.erb +25 -0
- data/app/views/solid_queue_monitor/jobs/_arguments.html.erb +9 -0
- data/app/views/solid_queue_monitor/jobs/_error.html.erb +26 -0
- data/app/views/solid_queue_monitor/jobs/_execution_history.html.erb +25 -0
- data/app/views/solid_queue_monitor/jobs/_header.html.erb +37 -0
- data/app/views/solid_queue_monitor/jobs/_metadata.html.erb +22 -0
- data/app/views/solid_queue_monitor/jobs/_raw_data.html.erb +11 -0
- data/app/views/solid_queue_monitor/jobs/_timeline.html.erb +29 -0
- data/app/views/solid_queue_monitor/jobs/_timing.html.erb +9 -0
- data/app/views/solid_queue_monitor/jobs/_worker.html.erb +12 -0
- data/app/views/solid_queue_monitor/jobs/show.html.erb +22 -0
- data/app/views/solid_queue_monitor/overview/_chart.html.erb +1 -0
- data/app/views/solid_queue_monitor/overview/_recent_job_row.html.erb +26 -0
- data/app/views/solid_queue_monitor/overview/_recent_jobs.html.erb +31 -0
- data/app/views/solid_queue_monitor/overview/_stat_card.html.erb +4 -0
- data/app/views/solid_queue_monitor/overview/_stats.html.erb +11 -0
- data/app/views/solid_queue_monitor/overview/index.html.erb +9 -0
- data/app/views/solid_queue_monitor/queues/_job_row.html.erb +26 -0
- data/app/views/solid_queue_monitor/queues/_row.html.erb +33 -0
- data/app/views/solid_queue_monitor/queues/index.html.erb +18 -0
- data/app/views/solid_queue_monitor/queues/show.html.erb +63 -0
- data/app/views/solid_queue_monitor/ready_jobs/_row.html.erb +7 -0
- data/app/views/solid_queue_monitor/ready_jobs/index.html.erb +25 -0
- data/app/views/solid_queue_monitor/recurring_jobs/_row.html.erb +8 -0
- data/app/views/solid_queue_monitor/recurring_jobs/index.html.erb +26 -0
- data/app/views/solid_queue_monitor/scheduled_jobs/_row.html.erb +7 -0
- data/app/views/solid_queue_monitor/scheduled_jobs/index.html.erb +36 -0
- data/app/views/solid_queue_monitor/search/_completed_row.html.erb +6 -0
- data/app/views/solid_queue_monitor/search/_failed_row.html.erb +6 -0
- data/app/views/solid_queue_monitor/search/_job_row.html.erb +9 -0
- data/app/views/solid_queue_monitor/search/_recurring_row.html.erb +6 -0
- data/app/views/solid_queue_monitor/search/_section.html.erb +25 -0
- data/app/views/solid_queue_monitor/search/index.html.erb +23 -0
- data/app/views/solid_queue_monitor/shared/_filters.html.erb +48 -0
- data/app/views/solid_queue_monitor/shared/_flash.html.erb +17 -0
- data/app/views/solid_queue_monitor/shared/_footer.html.erb +3 -0
- data/app/views/solid_queue_monitor/shared/_header.html.erb +81 -0
- data/app/views/solid_queue_monitor/shared/_jobs_table.html.erb +20 -0
- data/app/views/solid_queue_monitor/shared/_pagination.html.erb +25 -0
- data/app/views/solid_queue_monitor/workers/_row.html.erb +22 -0
- data/app/views/solid_queue_monitor/workers/index.html.erb +82 -0
- data/config/routes.rb +6 -1
- data/lib/solid_queue_monitor/engine.rb +2 -0
- data/lib/solid_queue_monitor/version.rb +1 -1
- metadata +57 -17
- data/app/presenters/solid_queue_monitor/base_presenter.rb +0 -211
- data/app/presenters/solid_queue_monitor/failed_jobs_presenter.rb +0 -225
- data/app/presenters/solid_queue_monitor/in_progress_jobs_presenter.rb +0 -84
- data/app/presenters/solid_queue_monitor/job_details_presenter.rb +0 -707
- data/app/presenters/solid_queue_monitor/jobs_presenter.rb +0 -144
- data/app/presenters/solid_queue_monitor/queue_details_presenter.rb +0 -195
- data/app/presenters/solid_queue_monitor/queues_presenter.rb +0 -89
- data/app/presenters/solid_queue_monitor/ready_jobs_presenter.rb +0 -81
- data/app/presenters/solid_queue_monitor/recurring_jobs_presenter.rb +0 -81
- data/app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb +0 -178
- data/app/presenters/solid_queue_monitor/search_results_presenter.rb +0 -190
- data/app/presenters/solid_queue_monitor/stats_presenter.rb +0 -36
- data/app/presenters/solid_queue_monitor/workers_presenter.rb +0 -325
- data/app/services/solid_queue_monitor/chart_presenter.rb +0 -239
- data/app/services/solid_queue_monitor/html_generator.rb +0 -427
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SolidQueueMonitor
|
|
4
|
-
class ScheduledJobsPresenter < BasePresenter
|
|
5
|
-
include Rails.application.routes.url_helpers
|
|
6
|
-
include SolidQueueMonitor::Engine.routes.url_helpers
|
|
7
|
-
|
|
8
|
-
def initialize(jobs, current_page: 1, total_pages: 1, filters: {}, sort: {}, nonce: nil)
|
|
9
|
-
@jobs = jobs
|
|
10
|
-
@current_page = current_page
|
|
11
|
-
@total_pages = total_pages
|
|
12
|
-
@filters = filters
|
|
13
|
-
@sort = sort
|
|
14
|
-
@nonce = nonce
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def render
|
|
18
|
-
section_wrapper('Scheduled Jobs', generate_filter_form + generate_table_with_actions)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def script_tag_open
|
|
24
|
-
@nonce ? %(<script nonce="#{@nonce}">) : '<script>'
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def generate_filter_form
|
|
28
|
-
<<-HTML
|
|
29
|
-
<div class="filter-form-container">
|
|
30
|
-
<form method="get" action="" class="filter-form">
|
|
31
|
-
<div class="filter-group">
|
|
32
|
-
<label for="class_name">Job Class:</label>
|
|
33
|
-
<input type="text" name="class_name" id="class_name" value="#{@filters[:class_name]}" placeholder="Filter by class name">
|
|
34
|
-
</div>
|
|
35
|
-
|
|
36
|
-
<div class="filter-group">
|
|
37
|
-
<label for="queue_name">Queue:</label>
|
|
38
|
-
<input type="text" name="queue_name" id="queue_name" value="#{@filters[:queue_name]}" placeholder="Filter by queue">
|
|
39
|
-
</div>
|
|
40
|
-
|
|
41
|
-
<div class="filter-group">
|
|
42
|
-
<label for="arguments">Arguments:</label>
|
|
43
|
-
<input type="text" name="arguments" id="arguments" value="#{@filters[:arguments]}" placeholder="Filter by arguments">
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<div class="filter-actions">
|
|
47
|
-
<button type="submit" class="filter-button">Apply Filters</button>
|
|
48
|
-
<a href="#{scheduled_jobs_path}" class="reset-button">Reset</a>
|
|
49
|
-
</div>
|
|
50
|
-
</form>
|
|
51
|
-
</div>
|
|
52
|
-
|
|
53
|
-
<div class="bulk-actions-bar">
|
|
54
|
-
<button type="button" class="action-button execute-button" id="execute-selected-top" disabled>Execute Selected</button>
|
|
55
|
-
<button type="button" class="action-button discard-button" id="reject-selected-top" disabled>Reject Selected</button>
|
|
56
|
-
</div>
|
|
57
|
-
HTML
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def generate_table_with_actions
|
|
61
|
-
<<-HTML
|
|
62
|
-
<form id="scheduled-jobs-form" method="POST">
|
|
63
|
-
#{generate_table}
|
|
64
|
-
</form>
|
|
65
|
-
#{script_tag_open}
|
|
66
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
67
|
-
const selectAllCheckbox = document.querySelector('th input[type="checkbox"]');
|
|
68
|
-
const jobCheckboxes = document.getElementsByName('job_ids[]');
|
|
69
|
-
const executeButton = document.getElementById('execute-selected-top');
|
|
70
|
-
const rejectButton = document.getElementById('reject-selected-top');
|
|
71
|
-
const form = document.getElementById('scheduled-jobs-form');
|
|
72
|
-
#{' '}
|
|
73
|
-
selectAllCheckbox.addEventListener('change', function() {
|
|
74
|
-
jobCheckboxes.forEach(checkbox => checkbox.checked = this.checked);
|
|
75
|
-
updateButtonStates();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
jobCheckboxes.forEach(checkbox => {
|
|
79
|
-
checkbox.addEventListener('change', function() {
|
|
80
|
-
selectAllCheckbox.checked = Array.from(jobCheckboxes).every(cb => cb.checked);
|
|
81
|
-
updateButtonStates();
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
#{' '}
|
|
85
|
-
// Add event listener for the execute button
|
|
86
|
-
executeButton.addEventListener('click', function() {
|
|
87
|
-
const selectedIds = Array.from(document.querySelectorAll('input[name="job_ids[]"]:checked')).map(cb => cb.value);
|
|
88
|
-
if (selectedIds.length === 0) return;
|
|
89
|
-
#{' '}
|
|
90
|
-
submitForm('#{execute_jobs_path}', selectedIds);
|
|
91
|
-
});
|
|
92
|
-
#{' '}
|
|
93
|
-
// Add event listener for the reject button
|
|
94
|
-
rejectButton.addEventListener('click', function() {
|
|
95
|
-
const selectedIds = Array.from(document.querySelectorAll('input[name="job_ids[]"]:checked')).map(cb => cb.value);
|
|
96
|
-
if (selectedIds.length === 0) return;
|
|
97
|
-
#{' '}
|
|
98
|
-
if (confirm('Are you sure you want to reject the selected jobs? This action cannot be undone.')) {
|
|
99
|
-
submitForm('#{reject_jobs_path}', selectedIds);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
#{' '}
|
|
103
|
-
function submitForm(actionUrl, selectedIds) {
|
|
104
|
-
// Uncheck all checkboxes to prevent duplicate submission
|
|
105
|
-
document.querySelectorAll('input[name="job_ids[]"]').forEach(checkbox => {
|
|
106
|
-
checkbox.checked = false;
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// Clear any existing hidden inputs
|
|
110
|
-
document.querySelectorAll('input[type="hidden"][name="job_ids[]"]').forEach(input => input.remove());
|
|
111
|
-
|
|
112
|
-
// Set form action
|
|
113
|
-
form.action = actionUrl;
|
|
114
|
-
|
|
115
|
-
// Add selected IDs as hidden inputs
|
|
116
|
-
selectedIds.forEach(id => {
|
|
117
|
-
const input = document.createElement('input');
|
|
118
|
-
input.type = 'hidden';
|
|
119
|
-
input.name = 'job_ids[]';
|
|
120
|
-
input.value = id;
|
|
121
|
-
form.appendChild(input);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Submit the form
|
|
125
|
-
form.submit();
|
|
126
|
-
}
|
|
127
|
-
#{' '}
|
|
128
|
-
function updateButtonStates() {
|
|
129
|
-
const checkboxes = document.getElementsByName('job_ids[]');
|
|
130
|
-
const checked = Array.from(checkboxes).some(cb => cb.checked);
|
|
131
|
-
executeButton.disabled = !checked;
|
|
132
|
-
rejectButton.disabled = !checked;
|
|
133
|
-
}
|
|
134
|
-
#{' '}
|
|
135
|
-
// Initialize button states
|
|
136
|
-
updateButtonStates();
|
|
137
|
-
});
|
|
138
|
-
</script>
|
|
139
|
-
HTML
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def generate_table
|
|
143
|
-
<<-HTML
|
|
144
|
-
<div class="table-container">
|
|
145
|
-
<table>
|
|
146
|
-
<thead>
|
|
147
|
-
<tr>
|
|
148
|
-
<th width="50"><input type="checkbox"></th>
|
|
149
|
-
#{sortable_header('class_name', 'Job')}
|
|
150
|
-
#{sortable_header('queue_name', 'Queue')}
|
|
151
|
-
#{sortable_header('scheduled_at', 'Scheduled At')}
|
|
152
|
-
<th>Arguments</th>
|
|
153
|
-
</tr>
|
|
154
|
-
</thead>
|
|
155
|
-
<tbody>
|
|
156
|
-
#{@jobs.map { |execution| generate_row(execution) }.join}
|
|
157
|
-
</tbody>
|
|
158
|
-
</table>
|
|
159
|
-
</div>
|
|
160
|
-
#{generate_pagination(@current_page, @total_pages)}
|
|
161
|
-
HTML
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def generate_row(execution)
|
|
165
|
-
<<-HTML
|
|
166
|
-
<tr>
|
|
167
|
-
<td>
|
|
168
|
-
<input type="checkbox" name="job_ids[]" value="#{execution.id}">
|
|
169
|
-
</td>
|
|
170
|
-
<td><a href="#{job_path(execution.job)}" class="job-class-link">#{execution.job.class_name}</a></td>
|
|
171
|
-
<td>#{queue_link(execution.queue_name)}</td>
|
|
172
|
-
<td>#{format_datetime(execution.scheduled_at)}</td>
|
|
173
|
-
<td>#{format_arguments(execution.job.arguments)}</td>
|
|
174
|
-
</tr>
|
|
175
|
-
HTML
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
end
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SolidQueueMonitor
|
|
4
|
-
class SearchResultsPresenter < BasePresenter
|
|
5
|
-
def initialize(query, results)
|
|
6
|
-
@query = query
|
|
7
|
-
@results = results
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def render
|
|
11
|
-
section_wrapper('Search Results', generate_content)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def generate_content
|
|
17
|
-
if @query.blank?
|
|
18
|
-
generate_empty_query_message
|
|
19
|
-
elsif total_count.zero?
|
|
20
|
-
generate_no_results_message
|
|
21
|
-
else
|
|
22
|
-
generate_results_summary + generate_all_sections
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def generate_empty_query_message
|
|
27
|
-
<<-HTML
|
|
28
|
-
<div class="empty-state">
|
|
29
|
-
<p>Enter a search term in the header to find jobs across all categories.</p>
|
|
30
|
-
</div>
|
|
31
|
-
HTML
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def generate_no_results_message
|
|
35
|
-
<<-HTML
|
|
36
|
-
<div class="empty-state">
|
|
37
|
-
<p>No results found for "#{escape_html(@query)}"</p>
|
|
38
|
-
<p class="results-summary">0 results</p>
|
|
39
|
-
</div>
|
|
40
|
-
HTML
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def generate_results_summary
|
|
44
|
-
<<-HTML
|
|
45
|
-
<div class="results-summary">
|
|
46
|
-
<p>Found #{total_count} #{total_count == 1 ? 'result' : 'results'} for "#{escape_html(@query)}"</p>
|
|
47
|
-
</div>
|
|
48
|
-
HTML
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def generate_all_sections
|
|
52
|
-
sections = []
|
|
53
|
-
sections << generate_ready_section if @results[:ready].any?
|
|
54
|
-
sections << generate_scheduled_section if @results[:scheduled].any?
|
|
55
|
-
sections << generate_failed_section if @results[:failed].any?
|
|
56
|
-
sections << generate_in_progress_section if @results[:in_progress].any?
|
|
57
|
-
sections << generate_completed_section if @results[:completed].any?
|
|
58
|
-
sections << generate_recurring_section if @results[:recurring].any?
|
|
59
|
-
sections.join
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def generate_ready_section
|
|
63
|
-
generate_section('Ready Jobs', @results[:ready]) do |execution|
|
|
64
|
-
generate_job_row(execution.job, execution.queue_name, execution.created_at)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def generate_scheduled_section
|
|
69
|
-
generate_section('Scheduled Jobs', @results[:scheduled]) do |execution|
|
|
70
|
-
generate_job_row(execution.job, execution.queue_name, execution.scheduled_at, 'Scheduled for')
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def generate_failed_section
|
|
75
|
-
generate_section('Failed Jobs', @results[:failed]) do |execution|
|
|
76
|
-
generate_failed_row(execution)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def generate_in_progress_section
|
|
81
|
-
generate_section('In Progress Jobs', @results[:in_progress]) do |execution|
|
|
82
|
-
generate_job_row(execution.job, execution.job.queue_name, execution.created_at, 'Started at')
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def generate_completed_section
|
|
87
|
-
generate_section('Completed Jobs', @results[:completed]) do |job|
|
|
88
|
-
generate_completed_row(job)
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def generate_recurring_section
|
|
93
|
-
generate_section('Recurring Tasks', @results[:recurring]) do |task|
|
|
94
|
-
generate_recurring_row(task)
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def generate_section(title, items, &block)
|
|
99
|
-
<<-HTML
|
|
100
|
-
<div class="search-results-section">
|
|
101
|
-
<h3>#{title} (#{items.size})</h3>
|
|
102
|
-
<div class="table-container">
|
|
103
|
-
<table>
|
|
104
|
-
<thead>
|
|
105
|
-
<tr>
|
|
106
|
-
#{section_headers(title)}
|
|
107
|
-
</tr>
|
|
108
|
-
</thead>
|
|
109
|
-
<tbody>
|
|
110
|
-
#{items.map(&block).join}
|
|
111
|
-
</tbody>
|
|
112
|
-
</table>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
115
|
-
HTML
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def section_headers(title)
|
|
119
|
-
case title
|
|
120
|
-
when 'Recurring Tasks'
|
|
121
|
-
'<th>Key</th><th>Class</th><th>Schedule</th><th>Queue</th>'
|
|
122
|
-
when 'Failed Jobs'
|
|
123
|
-
'<th>Job</th><th>Queue</th><th>Error</th><th>Failed At</th>'
|
|
124
|
-
when 'Completed Jobs'
|
|
125
|
-
'<th>Job</th><th>Queue</th><th>Arguments</th><th>Completed At</th>'
|
|
126
|
-
else
|
|
127
|
-
'<th>Job</th><th>Queue</th><th>Arguments</th><th>Time</th>'
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def generate_job_row(job, queue_name, time, time_label = 'Created at')
|
|
132
|
-
<<-HTML
|
|
133
|
-
<tr>
|
|
134
|
-
<td><a href="#{job_path(job)}" class="job-class-link">#{job.class_name}</a></td>
|
|
135
|
-
<td>#{queue_link(queue_name)}</td>
|
|
136
|
-
<td>#{format_arguments(job.arguments)}</td>
|
|
137
|
-
<td>
|
|
138
|
-
<span class="job-timestamp">#{time_label}: #{format_datetime(time)}</span>
|
|
139
|
-
</td>
|
|
140
|
-
</tr>
|
|
141
|
-
HTML
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def generate_failed_row(execution)
|
|
145
|
-
job = execution.job
|
|
146
|
-
<<-HTML
|
|
147
|
-
<tr>
|
|
148
|
-
<td><a href="#{job_path(job)}" class="job-class-link">#{job.class_name}</a></td>
|
|
149
|
-
<td>#{queue_link(job.queue_name)}</td>
|
|
150
|
-
<td><div class="error-message">#{escape_html(execution.error.to_s.truncate(100))}</div></td>
|
|
151
|
-
<td>
|
|
152
|
-
<span class="job-timestamp">#{format_datetime(execution.created_at)}</span>
|
|
153
|
-
</td>
|
|
154
|
-
</tr>
|
|
155
|
-
HTML
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def generate_completed_row(job)
|
|
159
|
-
<<-HTML
|
|
160
|
-
<tr>
|
|
161
|
-
<td><a href="#{job_path(job)}" class="job-class-link">#{job.class_name}</a></td>
|
|
162
|
-
<td>#{queue_link(job.queue_name)}</td>
|
|
163
|
-
<td>#{format_arguments(job.arguments)}</td>
|
|
164
|
-
<td>
|
|
165
|
-
<span class="job-timestamp">#{format_datetime(job.finished_at)}</span>
|
|
166
|
-
</td>
|
|
167
|
-
</tr>
|
|
168
|
-
HTML
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def generate_recurring_row(task)
|
|
172
|
-
<<-HTML
|
|
173
|
-
<tr>
|
|
174
|
-
<td><strong>#{task.key}</strong></td>
|
|
175
|
-
<td>#{task.class_name || '-'}</td>
|
|
176
|
-
<td><code>#{task.schedule}</code></td>
|
|
177
|
-
<td>#{queue_link(task.queue_name)}</td>
|
|
178
|
-
</tr>
|
|
179
|
-
HTML
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def total_count
|
|
183
|
-
@total_count ||= @results.values.sum(&:size)
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def escape_html(text)
|
|
187
|
-
text.to_s.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('"', '"')
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SolidQueueMonitor
|
|
4
|
-
class StatsPresenter < BasePresenter
|
|
5
|
-
def initialize(stats)
|
|
6
|
-
@stats = stats
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def render
|
|
10
|
-
<<-HTML
|
|
11
|
-
<div class="stats-container">
|
|
12
|
-
<h3>Queue Statistics</h3>
|
|
13
|
-
<div class="stats">
|
|
14
|
-
#{generate_stat_card('Active Jobs', @stats[:active_jobs])}
|
|
15
|
-
#{generate_stat_card('Ready', @stats[:ready])}
|
|
16
|
-
#{generate_stat_card('In Progress', @stats[:in_progress])}
|
|
17
|
-
#{generate_stat_card('Scheduled', @stats[:scheduled])}
|
|
18
|
-
#{generate_stat_card('Recurring', @stats[:recurring])}
|
|
19
|
-
#{generate_stat_card('Failed', @stats[:failed])}
|
|
20
|
-
</div>
|
|
21
|
-
</div>
|
|
22
|
-
HTML
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
def generate_stat_card(title, value)
|
|
28
|
-
<<-HTML
|
|
29
|
-
<div class="stat-card">
|
|
30
|
-
<h3>#{title}</h3>
|
|
31
|
-
<p>#{value}</p>
|
|
32
|
-
</div>
|
|
33
|
-
HTML
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|