que-view 0.3.0 → 0.3.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/README.md +0 -10
- data/app/assets/stylesheets/que/view/application.css +99 -60
- data/app/controllers/que/view/jobs_controller.rb +34 -27
- data/app/controllers/que/view/welcome_controller.rb +1 -1
- data/app/views/layouts/que/view/application.html.erb +12 -4
- data/app/views/que/view/jobs/index.html.erb +50 -46
- data/app/views/que/view/welcome/index.html.erb +55 -33
- data/lib/que/view/dsl.rb +81 -30
- data/lib/que/view/version.rb +1 -1
- data/lib/que/view.rb +3 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d07972f8adf809f3f10f495824376c64724e1526e7afd6d85a05da93632741e0
|
4
|
+
data.tar.gz: 95d6ce6e4d1340dcb550a1ae4b85560f19a18de0b9c0dec1eea5ce5a44726ddf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9553edb70775fc2fa268ad2ec3e14d3ab51c3858b54363efbb0d6b8aae85f00d450d99e925cd576dc7117f1ff784bbbe664d763305123598acc35bda57e38e8b
|
7
|
+
data.tar.gz: ce8bc2cbfef7844814f60125bbad375bf676bc2ef14180d5a2bdc2b4a9fd5cc3ac1920f5b5f40b540a2e600ee4107c247c3b89f90d3e7662725de1a6539c2461
|
data/README.md
CHANGED
@@ -53,15 +53,5 @@ Add this line to assets/config/manifest.js
|
|
53
53
|
//= link que/view/application.css
|
54
54
|
```
|
55
55
|
|
56
|
-
## TODO
|
57
|
-
|
58
|
-
- [X] rescheduling jobs
|
59
|
-
- [X] deleting jobs
|
60
|
-
- [X] better styles for UI
|
61
|
-
- [X] rendering running jobs
|
62
|
-
- [ ] searching/filtering through jobs by name, queue
|
63
|
-
- [X] pagination for jobs list page
|
64
|
-
- [ ] tests
|
65
|
-
|
66
56
|
## License
|
67
57
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -21,71 +21,63 @@
|
|
21
21
|
}
|
22
22
|
|
23
23
|
html, body {
|
24
|
-
padding: 0;
|
25
24
|
margin: 0;
|
25
|
+
padding: 0;
|
26
|
+
}
|
27
|
+
|
28
|
+
body {
|
29
|
+
background: #fafafa;
|
26
30
|
}
|
27
31
|
|
28
32
|
.row {
|
29
33
|
display: flex;
|
30
|
-
flex-direction: row;
|
31
|
-
margin: 0 auto;
|
32
|
-
max-width: 75rem;
|
33
|
-
width: 100%;
|
34
34
|
}
|
35
35
|
|
36
|
-
.
|
37
|
-
flex: 1;
|
36
|
+
.content {
|
38
37
|
padding: 1rem;
|
39
38
|
}
|
40
39
|
|
41
|
-
.
|
42
|
-
|
43
|
-
margin-bottom: 1rem;
|
44
|
-
}
|
45
|
-
|
46
|
-
table th, table td {
|
47
|
-
padding: .25rem .75rem;
|
48
|
-
border-bottom: 1px solid #BBB;
|
49
|
-
}
|
50
|
-
|
51
|
-
table th {
|
52
|
-
text-align: left;
|
53
|
-
}
|
54
|
-
|
55
|
-
table tbody tr:nth-of-type(2n), table tbody tr:hover {
|
56
|
-
background: #EEE;
|
57
|
-
}
|
58
|
-
|
59
|
-
table tbody tr:nth-of-type(2n):hover {
|
60
|
-
background: #DDD;
|
61
|
-
}
|
62
|
-
|
63
|
-
table p {
|
64
|
-
margin: 0;
|
40
|
+
.actions {
|
41
|
+
display: flex;
|
65
42
|
}
|
66
43
|
|
67
|
-
.
|
44
|
+
.dashboard-row {
|
68
45
|
display: flex;
|
46
|
+
margin: 0 auto;
|
47
|
+
width: 100%;
|
48
|
+
border: 1px solid #a8a29e;
|
49
|
+
border-radius: .25rem;
|
50
|
+
overflow: hidden;
|
69
51
|
}
|
70
52
|
|
71
|
-
.dashboard-stat {
|
53
|
+
.dashboard-row .dashboard-stat {
|
72
54
|
text-align: center;
|
73
55
|
display: table;
|
74
56
|
width: 100%;
|
57
|
+
background: #fff;
|
58
|
+
border-right: 1px solid #a8a29e;
|
75
59
|
}
|
76
60
|
|
77
|
-
.dashboard-stat
|
61
|
+
.dashboard-row .dashboard-stat:hover {
|
62
|
+
background: #f5f5f4;
|
63
|
+
}
|
64
|
+
|
65
|
+
.dashboard-row .dashboard-stat:nth-last-of-type(1) {
|
66
|
+
border: none;
|
67
|
+
}
|
68
|
+
|
69
|
+
.dashboard-row .dashboard-stat a {
|
78
70
|
text-decoration: none;
|
79
71
|
}
|
80
72
|
|
81
|
-
.dashboard-stat .cell {
|
73
|
+
.dashboard-row .dashboard-stat .cell {
|
82
74
|
display: flex;
|
83
75
|
flex-direction: column;
|
84
76
|
justify-content: center;
|
85
|
-
height:
|
77
|
+
height: 150px;
|
86
78
|
}
|
87
79
|
|
88
|
-
.dashboard-stat h2 {
|
80
|
+
.dashboard-row .dashboard-stat h2 {
|
89
81
|
color: #222;
|
90
82
|
font-size: 1rem;
|
91
83
|
font-weight: normal;
|
@@ -93,24 +85,23 @@ table p {
|
|
93
85
|
margin: 0;
|
94
86
|
}
|
95
87
|
|
96
|
-
.dashboard-stat.running {
|
97
|
-
background: #CFD0C1;
|
98
|
-
}
|
99
|
-
|
100
|
-
.dashboard-stat.scheduled {
|
101
|
-
background: #828E8C;
|
102
|
-
}
|
103
|
-
|
104
|
-
.dashboard-stat.failing {
|
105
|
-
background: #E8866C;
|
106
|
-
}
|
107
|
-
|
108
88
|
.dashboard-value {
|
109
|
-
font-size:
|
110
|
-
line-height:
|
89
|
+
font-size: 2rem;
|
90
|
+
line-height: 2rem;
|
111
91
|
color: black;
|
112
92
|
}
|
113
93
|
|
94
|
+
.btn-primary {
|
95
|
+
cursor: pointer;
|
96
|
+
border: none;
|
97
|
+
border-radius: .25rem;
|
98
|
+
background: #fde68a;
|
99
|
+
border: 1px solid #fcd34d;
|
100
|
+
border-radius: .25rem;
|
101
|
+
padding: .25rem .5rem;
|
102
|
+
margin-right: .5rem;
|
103
|
+
}
|
104
|
+
|
114
105
|
.btn-danger {
|
115
106
|
cursor: pointer;
|
116
107
|
border: none;
|
@@ -144,18 +135,21 @@ table p {
|
|
144
135
|
|
145
136
|
.navigation {
|
146
137
|
display: flex;
|
147
|
-
padding:
|
148
|
-
background: #
|
149
|
-
|
138
|
+
padding: 0 1rem;
|
139
|
+
background: #fff;
|
140
|
+
border-bottom: 1px solid #e7e5e4;
|
141
|
+
color: #000;
|
150
142
|
}
|
151
143
|
|
152
144
|
.navigation h1 {
|
153
145
|
margin: 0 2rem 0 0;
|
146
|
+
display: flex;
|
147
|
+
align-items: center;
|
154
148
|
}
|
155
149
|
|
156
150
|
.navigation h1 a {
|
157
151
|
text-decoration: none;
|
158
|
-
color: #
|
152
|
+
color: #000;
|
159
153
|
}
|
160
154
|
|
161
155
|
.navigation .navigation-section {
|
@@ -178,17 +172,62 @@ table p {
|
|
178
172
|
|
179
173
|
.navigation .navigation-section ul li a {
|
180
174
|
display: inline-block;
|
181
|
-
padding:
|
175
|
+
padding: 1rem;
|
182
176
|
text-decoration: none;
|
183
|
-
color: #
|
177
|
+
color: #000;
|
178
|
+
}
|
179
|
+
|
180
|
+
.navigation .navigation-section ul li.active {
|
181
|
+
background: #e7e5e4;
|
182
|
+
}
|
183
|
+
|
184
|
+
.search-form .form-select {
|
185
|
+
margin-right: 1rem;
|
186
|
+
padding: .5rem 1rem;
|
187
|
+
border: 1px solid #e7e5e4;
|
188
|
+
border-radius: .25rem;
|
189
|
+
background: #fff;
|
190
|
+
}
|
191
|
+
|
192
|
+
table {
|
193
|
+
width: 100%;
|
194
|
+
margin-bottom: 1rem;
|
195
|
+
background: #fff;
|
196
|
+
border: 1px solid #e7e5e4;
|
197
|
+
border-bottom: none;
|
198
|
+
border-radius: .25rem;
|
199
|
+
}
|
200
|
+
|
201
|
+
table th, table td {
|
202
|
+
padding: .5rem .75rem;
|
203
|
+
border-bottom: 1px solid #e7e5e4;
|
184
204
|
}
|
185
205
|
|
186
|
-
|
187
|
-
text-
|
206
|
+
table th {
|
207
|
+
text-align: left;
|
208
|
+
font-weight: normal;
|
209
|
+
text-transform: uppercase;
|
210
|
+
}
|
211
|
+
|
212
|
+
table tbody tr:nth-of-type(2n), table tbody tr:hover {
|
213
|
+
background: #EEE;
|
214
|
+
}
|
215
|
+
|
216
|
+
table tbody tr:nth-of-type(2n):hover {
|
217
|
+
background: #DDD;
|
218
|
+
}
|
219
|
+
|
220
|
+
table p {
|
221
|
+
margin: 0;
|
188
222
|
}
|
189
223
|
|
190
224
|
@media screen and (max-width: 768px) {
|
191
|
-
.row {
|
225
|
+
.dashboard-row {
|
192
226
|
flex-direction: column;
|
193
227
|
}
|
228
|
+
|
229
|
+
.dashboard-row .dashboard-stat {
|
230
|
+
border-right: none;
|
231
|
+
border-bottom: 1px solid #a8a29e;
|
232
|
+
}
|
194
233
|
}
|
@@ -5,10 +5,12 @@ module Que
|
|
5
5
|
class JobsController < Que::View::ApplicationController
|
6
6
|
PER_PAGE = 20
|
7
7
|
|
8
|
+
before_action :find_queue_names, only: %i[index]
|
9
|
+
before_action :find_job_names, only: %i[index]
|
8
10
|
before_action :find_job, only: %i[show]
|
9
11
|
|
10
12
|
def index
|
11
|
-
@jobs = find_jobs(
|
13
|
+
@jobs = find_jobs(index_params)
|
12
14
|
paginate
|
13
15
|
end
|
14
16
|
|
@@ -48,6 +50,25 @@ module Que
|
|
48
50
|
|
49
51
|
private
|
50
52
|
|
53
|
+
def find_queue_names
|
54
|
+
@queue_names = [
|
55
|
+
['All queues', nil]
|
56
|
+
] + ::Que::View.fetch_queue_names
|
57
|
+
end
|
58
|
+
|
59
|
+
def find_job_names
|
60
|
+
@job_names = [
|
61
|
+
['All jobs', nil]
|
62
|
+
] + ::Que::View.fetch_job_names(params[:queue_name])
|
63
|
+
end
|
64
|
+
|
65
|
+
def find_job
|
66
|
+
@job = ::Que::View.fetch_job(params[:id])[0]
|
67
|
+
return if @job
|
68
|
+
|
69
|
+
redirect_to root_path, notice: 'Job is not found'
|
70
|
+
end
|
71
|
+
|
51
72
|
def paginate
|
52
73
|
return if %w[failing scheduled].exclude?(params[:status])
|
53
74
|
return unless @jobs.any?
|
@@ -61,24 +82,19 @@ module Que
|
|
61
82
|
)
|
62
83
|
end
|
63
84
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
def find_jobs(status)
|
72
|
-
case status&.to_sym
|
73
|
-
when :running then ::Que::View.fetch_running_jobs(search)
|
74
|
-
when :failing then ::Que::View.fetch_failing_jobs(PER_PAGE, offset, search)
|
75
|
-
when :scheduled then ::Que::View.fetch_scheduled_jobs(PER_PAGE, offset, search)
|
85
|
+
def find_jobs(params)
|
86
|
+
case params[:status]&.to_sym
|
87
|
+
when :running then ::Que::View.fetch_running_jobs(params)
|
88
|
+
when :failing then ::Que::View.fetch_failing_jobs(PER_PAGE, offset, params)
|
89
|
+
when :scheduled then ::Que::View.fetch_scheduled_jobs(PER_PAGE, offset, params)
|
90
|
+
when :finished then ::Que::View.fetch_finished_jobs(PER_PAGE, offset, params)
|
91
|
+
when :expired then ::Que::View.fetch_expired_jobs(PER_PAGE, offset, params)
|
76
92
|
else []
|
77
93
|
end
|
78
94
|
end
|
79
95
|
|
80
96
|
def find_jobs_total_amount(status)
|
81
|
-
::Que::View.fetch_dashboard_stats
|
97
|
+
::Que::View.fetch_dashboard_stats[0][status&.to_sym]
|
82
98
|
end
|
83
99
|
|
84
100
|
def reschedule_all_jobs(status, time)
|
@@ -97,19 +113,6 @@ module Que
|
|
97
113
|
end
|
98
114
|
end
|
99
115
|
|
100
|
-
def search
|
101
|
-
return '%' unless search_param
|
102
|
-
|
103
|
-
"%#{search_param}%"
|
104
|
-
end
|
105
|
-
|
106
|
-
def search_param
|
107
|
-
sanitised = (params[:search] || '').gsub(/[^0-9a-z:]/i, '')
|
108
|
-
return if sanitised.empty?
|
109
|
-
|
110
|
-
sanitised
|
111
|
-
end
|
112
|
-
|
113
116
|
def offset
|
114
117
|
(page - 1) * PER_PAGE
|
115
118
|
end
|
@@ -117,6 +120,10 @@ module Que
|
|
117
120
|
def page
|
118
121
|
(params[:page] || 1).to_i
|
119
122
|
end
|
123
|
+
|
124
|
+
def index_params
|
125
|
+
params.permit(:status, :queue_name, :job_name).to_h.symbolize_keys
|
126
|
+
end
|
120
127
|
end
|
121
128
|
end
|
122
129
|
end
|
@@ -11,15 +11,21 @@
|
|
11
11
|
<h1><%= link_to 'Que View', root_path %></h1>
|
12
12
|
<section class="navigation-section">
|
13
13
|
<ul>
|
14
|
-
<li class="<%= 'active' if params[:status] == 'running' %>">
|
15
|
-
<%= link_to 'Running', jobs_path(status: 'running') %>
|
16
|
-
</li>
|
17
14
|
<li class="<%= 'active' if params[:status] == 'scheduled' %>">
|
18
15
|
<%= link_to 'Scheduled', jobs_path(status: 'scheduled') %>
|
19
16
|
</li>
|
20
17
|
<li class="<%= 'active' if params[:status] == 'failing' %>">
|
21
18
|
<%= link_to 'Failing', jobs_path(status: 'failing') %>
|
22
19
|
</li>
|
20
|
+
<li class="<%= 'active' if params[:status] == 'running' %>">
|
21
|
+
<%= link_to 'Running', jobs_path(status: 'running') %>
|
22
|
+
</li>
|
23
|
+
<li class="<%= 'active' if params[:status] == 'finished' %>">
|
24
|
+
<%= link_to 'Finished', jobs_path(status: 'finished') %>
|
25
|
+
</li>
|
26
|
+
<li class="<%= 'active' if params[:status] == 'expired' %>">
|
27
|
+
<%= link_to 'Expired', jobs_path(status: 'expired') %>
|
28
|
+
</li>
|
23
29
|
</ul>
|
24
30
|
<ul class="version">
|
25
31
|
<li>Que <%= Que::VERSION %></li>
|
@@ -28,6 +34,8 @@
|
|
28
34
|
</ul>
|
29
35
|
</section>
|
30
36
|
</nav>
|
31
|
-
|
37
|
+
<section class="content">
|
38
|
+
<%= yield %>
|
39
|
+
</section>
|
32
40
|
</body>
|
33
41
|
</html>
|
@@ -1,3 +1,11 @@
|
|
1
|
+
<%= form_with url: jobs_path, method: :get, class: 'search-form' do |form| %>
|
2
|
+
<%= form.hidden_field :status, value: params[:status] %>
|
3
|
+
<div class="row">
|
4
|
+
<%= form.select :queue_name, options_for_select(@queue_names, params[:queue_name]), {}, class: 'form-select' %>
|
5
|
+
<%= form.select :job_name, options_for_select(@job_names, params[:job_name]), {}, class: 'form-select' %>
|
6
|
+
<%= form.submit 'Search', class: 'btn-primary' %>
|
7
|
+
</div>
|
8
|
+
<% end %>
|
1
9
|
<% if @jobs.any? %>
|
2
10
|
<% if @pagination %>
|
3
11
|
<div class="row pagination">
|
@@ -18,61 +26,57 @@
|
|
18
26
|
<% end %>
|
19
27
|
</div>
|
20
28
|
<% end %>
|
21
|
-
<
|
22
|
-
<
|
23
|
-
<
|
29
|
+
<table cellspacing="0" class="jobs-list">
|
30
|
+
<thead>
|
31
|
+
<tr>
|
32
|
+
<th>ID</th>
|
33
|
+
<th>Job</th>
|
34
|
+
<th>Queue</th>
|
35
|
+
<th>Run at</th>
|
36
|
+
<th>Arguments</th>
|
37
|
+
<% if params[:status] == 'failing' %>
|
38
|
+
<th>Failures</th>
|
39
|
+
<th>Error</th>
|
40
|
+
<% end %>
|
41
|
+
<% if %w[failing scheduled].include?(params[:status]) %>
|
42
|
+
<th>Actions</th>
|
43
|
+
<% end %>
|
44
|
+
</tr>
|
45
|
+
</thead>
|
46
|
+
<tbody>
|
47
|
+
<% @jobs.each do |job| %>
|
24
48
|
<tr>
|
25
|
-
<
|
26
|
-
<
|
27
|
-
<
|
28
|
-
<
|
29
|
-
<
|
49
|
+
<td><%= link_to job[:id], job_path(job[:id]) %></td>
|
50
|
+
<td><%= humanized_job_class(job) %></td>
|
51
|
+
<td><%= job[:queue] %></td>
|
52
|
+
<td><%= job[:run_at].strftime("%Y-%m-%d %H:%M:%S") %></td>
|
53
|
+
<td>
|
54
|
+
<% job.dig(:args, 0, :arguments).each do |argument| %>
|
55
|
+
<p><%= argument %></p>
|
56
|
+
<% end %>
|
57
|
+
</td>
|
30
58
|
<% if params[:status] == 'failing' %>
|
31
|
-
<
|
32
|
-
<
|
59
|
+
<td><%= job[:error_count] %></td>
|
60
|
+
<td><%= format_error(job) %></td>
|
33
61
|
<% end %>
|
34
62
|
<% if %w[failing scheduled].include?(params[:status]) %>
|
35
|
-
<th></th>
|
36
|
-
<% end %>
|
37
|
-
</tr>
|
38
|
-
</thead>
|
39
|
-
<tbody>
|
40
|
-
<% @jobs.each do |job| %>
|
41
|
-
<tr>
|
42
|
-
<td><%= link_to job[:id], job_path(job[:id]) %></td>
|
43
|
-
<td><%= job[:run_at].strftime("%Y-%m-%d %H:%M:%S") %></td>
|
44
|
-
<td><%= humanized_job_class(job) %></td>
|
45
|
-
<td><%= job[:queue] %></td>
|
46
63
|
<td>
|
47
|
-
|
48
|
-
|
49
|
-
|
64
|
+
<div class="actions">
|
65
|
+
<%= button_to 'Run', job_path(job[:id]), class: 'btn-danger', method: :patch, onclick: "return confirm('Are you sure you wish to reschedule job?')" %>
|
66
|
+
<%= button_to 'Delete', job_path(job[:id]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete job?')" %>
|
67
|
+
</div>
|
50
68
|
</td>
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
<%= button_to 'Delete', job_path(job[:id]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete job?')" %>
|
60
|
-
</div>
|
61
|
-
</td>
|
62
|
-
<% end %>
|
63
|
-
</tr>
|
64
|
-
<% end %>
|
65
|
-
</tbody>
|
66
|
-
</table>
|
69
|
+
<% end %>
|
70
|
+
</tr>
|
71
|
+
<% end %>
|
72
|
+
</tbody>
|
73
|
+
</table>
|
74
|
+
<div class="row">
|
75
|
+
<%= button_to 'Run All', reschedule_all_jobs_path(status: params[:status]), class: 'btn-danger', method: :post, onclick: "return confirm('Are you sure you wish to reschedule all jobs?')" %>
|
76
|
+
<%= button_to 'Delete All', destroy_all_jobs_path(status: params[:status]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete all jobs?')" %>
|
67
77
|
</div>
|
68
78
|
<% else %>
|
69
79
|
<div class="row">
|
70
80
|
<p>No jobs found</p>
|
71
81
|
</div>
|
72
82
|
<% end %>
|
73
|
-
<% if @pagination&.count&.positive? %>
|
74
|
-
<div class="row">
|
75
|
-
<%= button_to 'Run All', reschedule_all_jobs_path(status: params[:status]), class: 'btn-danger', method: :post, onclick: "return confirm('Are you sure you wish to reschedule all jobs?')" %>
|
76
|
-
<%= button_to 'Delete All', destroy_all_jobs_path(status: params[:status]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete all jobs?')" %>
|
77
|
-
</div>
|
78
|
-
<% end %>
|
@@ -1,38 +1,60 @@
|
|
1
|
-
<div class="row">
|
2
|
-
<div class="
|
3
|
-
<div class="
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
<%= @dashboard_stats[:running] %>
|
9
|
-
</span>
|
10
|
-
</div>
|
11
|
-
<% end %>
|
1
|
+
<div class="dashboard-row">
|
2
|
+
<div class="dashboard-stat">
|
3
|
+
<div class="cell">
|
4
|
+
<h2>Total</h2>
|
5
|
+
<span class="dashboard-value">
|
6
|
+
<%= @dashboard_stats[:total] %>
|
7
|
+
</span>
|
12
8
|
</div>
|
13
9
|
</div>
|
14
|
-
<div class="
|
15
|
-
|
16
|
-
|
17
|
-
<
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
<% end %>
|
24
|
-
</div>
|
10
|
+
<div class="dashboard-stat">
|
11
|
+
<%= link_to jobs_path(status: 'scheduled') do %>
|
12
|
+
<div class="cell">
|
13
|
+
<h2>Scheduled</h2>
|
14
|
+
<span class="dashboard-value">
|
15
|
+
<%= @dashboard_stats[:scheduled] %>
|
16
|
+
</span>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
25
19
|
</div>
|
26
|
-
<div class="
|
27
|
-
|
28
|
-
|
29
|
-
<
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
20
|
+
<div class="dashboard-stat">
|
21
|
+
<%= link_to jobs_path(status: 'failing') do %>
|
22
|
+
<div class="cell">
|
23
|
+
<h2>Failing</h2>
|
24
|
+
<span class="dashboard-value">
|
25
|
+
<%= @dashboard_stats[:failing] %>
|
26
|
+
</span>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
<div class="dashboard-stat">
|
31
|
+
<%= link_to jobs_path(status: 'running') do %>
|
32
|
+
<div class="cell">
|
33
|
+
<h2>Running</h2>
|
34
|
+
<span class="dashboard-value">
|
35
|
+
<%= @dashboard_stats[:running] %>
|
36
|
+
</span>
|
37
|
+
</div>
|
38
|
+
<% end %>
|
39
|
+
</div>
|
40
|
+
<div class="dashboard-stat">
|
41
|
+
<%= link_to jobs_path(status: 'finished') do %>
|
42
|
+
<div class="cell">
|
43
|
+
<h2>Finished</h2>
|
44
|
+
<span class="dashboard-value">
|
45
|
+
<%= @dashboard_stats[:finished] %>
|
46
|
+
</span>
|
47
|
+
</div>
|
48
|
+
<% end %>
|
49
|
+
</div>
|
50
|
+
<div class="dashboard-stat">
|
51
|
+
<%= link_to jobs_path(status: 'expired') do %>
|
52
|
+
<div class="cell">
|
53
|
+
<h2>Expired</h2>
|
54
|
+
<span class="dashboard-value">
|
55
|
+
<%= @dashboard_stats[:expired] %>
|
56
|
+
</span>
|
57
|
+
</div>
|
58
|
+
<% end %>
|
37
59
|
</div>
|
38
60
|
</div>
|
data/lib/que/view/dsl.rb
CHANGED
@@ -4,8 +4,20 @@ module Que
|
|
4
4
|
module View
|
5
5
|
# rubocop: disable Metrics/ClassLength
|
6
6
|
class DSL
|
7
|
-
def fetch_dashboard_stats
|
8
|
-
execute(fetch_dashboard_stats_sql
|
7
|
+
def fetch_dashboard_stats
|
8
|
+
execute(fetch_dashboard_stats_sql)
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch_queue_names
|
12
|
+
execute(fetch_queue_names_sql).map { |queues_data|
|
13
|
+
["#{queues_data[:queue_name]} (#{queues_data[:count_all]})", queues_data[:queue_name]]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch_job_names(...)
|
18
|
+
execute(fetch_job_names_sql(...)).map { |jobs_data|
|
19
|
+
["#{jobs_data[:job_name]} (#{jobs_data[:count_all]})", jobs_data[:job_name]]
|
20
|
+
}
|
9
21
|
end
|
10
22
|
|
11
23
|
def fetch_running_jobs(...)
|
@@ -20,6 +32,14 @@ module Que
|
|
20
32
|
execute(fetch_scheduled_jobs_sql(...))
|
21
33
|
end
|
22
34
|
|
35
|
+
def fetch_finished_jobs(...)
|
36
|
+
execute(fetch_finished_jobs_sql(...))
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_expired_jobs(...)
|
40
|
+
execute(fetch_expired_jobs_sql(...))
|
41
|
+
end
|
42
|
+
|
23
43
|
def fetch_job(...)
|
24
44
|
execute(fetch_job_sql(...))
|
25
45
|
end
|
@@ -51,46 +71,82 @@ module Que
|
|
51
71
|
private
|
52
72
|
|
53
73
|
# rubocop: disable Metrics/MethodLength
|
54
|
-
def fetch_dashboard_stats_sql
|
74
|
+
def fetch_dashboard_stats_sql
|
55
75
|
<<-SQL.squish
|
56
|
-
SELECT count(*)
|
57
|
-
count(locks.job_id)
|
76
|
+
SELECT count(*) AS total,
|
77
|
+
count(locks.job_id) AS running,
|
58
78
|
coalesce(sum((error_count > 0 AND locks.job_id IS NULL)::int), 0) AS failing,
|
59
|
-
coalesce(sum((error_count = 0 AND locks.job_id IS NULL)::int), 0) AS scheduled
|
79
|
+
coalesce(sum((error_count = 0 AND locks.job_id IS NULL)::int), 0) AS scheduled,
|
80
|
+
coalesce(sum((finished_at IS NOT NULL)::int), 0) AS finished,
|
81
|
+
coalesce(sum((expired_at IS NOT NULL)::int), 0) AS expired
|
60
82
|
FROM que_jobs
|
61
83
|
LEFT JOIN (
|
62
84
|
SELECT (classid::bigint << 32) + objid::bigint AS job_id
|
63
85
|
FROM pg_locks
|
64
86
|
WHERE locktype = 'advisory'
|
65
87
|
) locks ON (que_jobs.id=locks.job_id)
|
66
|
-
WHERE
|
67
|
-
job_class ILIKE ('#{search}')
|
68
|
-
OR que_jobs.args #>> '{0, job_class}' ILIKE ('#{search}')
|
69
88
|
SQL
|
70
89
|
end
|
71
90
|
|
72
|
-
def
|
91
|
+
def fetch_queue_names_sql
|
73
92
|
<<-SQL.squish
|
74
|
-
SELECT
|
93
|
+
SELECT COUNT(*) AS count_all, queue AS queue_name
|
75
94
|
FROM que_jobs
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
95
|
+
GROUP BY queue
|
96
|
+
SQL
|
97
|
+
end
|
98
|
+
|
99
|
+
def fetch_job_names_sql(queue_name)
|
100
|
+
<<-SQL.squish
|
101
|
+
SELECT COUNT(*) AS count_all, args #>> '{0, job_class}' AS job_name
|
102
|
+
FROM que_jobs
|
103
|
+
#{queue_name.present? ? "WHERE queue = '#{queue_name}'" : ""}
|
104
|
+
GROUP BY args #>> '{0, job_class}'
|
105
|
+
SQL
|
106
|
+
end
|
107
|
+
|
108
|
+
def fetch_failing_jobs_sql(per_page, offset, params)
|
109
|
+
where_condition = <<-SQL.squish
|
81
110
|
WHERE locks.job_id IS NULL
|
82
111
|
AND error_count > 0
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
112
|
+
#{search_condition(params)}
|
113
|
+
SQL
|
114
|
+
fetch_jobs_sql(per_page, offset, where_condition)
|
115
|
+
end
|
116
|
+
|
117
|
+
def fetch_scheduled_jobs_sql(per_page, offset, params)
|
118
|
+
where_condition = <<-SQL.squish
|
119
|
+
WHERE locks.job_id IS NULL
|
120
|
+
AND error_count = 0
|
121
|
+
#{search_condition(params)}
|
90
122
|
SQL
|
123
|
+
fetch_jobs_sql(per_page, offset, where_condition)
|
91
124
|
end
|
92
125
|
|
93
|
-
def
|
126
|
+
def fetch_finished_jobs_sql(per_page, offset, params)
|
127
|
+
where_condition = <<-SQL.squish
|
128
|
+
WHERE finished_at IS NOT NULL
|
129
|
+
#{search_condition(params)}
|
130
|
+
SQL
|
131
|
+
fetch_jobs_sql(per_page, offset, where_condition)
|
132
|
+
end
|
133
|
+
|
134
|
+
def fetch_expired_jobs_sql(per_page, offset, params)
|
135
|
+
where_condition = <<-SQL.squish
|
136
|
+
WHERE expired_at IS NOT NULL
|
137
|
+
#{search_condition(params)}
|
138
|
+
SQL
|
139
|
+
fetch_jobs_sql(per_page, offset, where_condition)
|
140
|
+
end
|
141
|
+
|
142
|
+
def search_condition(params)
|
143
|
+
result = ''
|
144
|
+
result += "AND queue = '#{params[:queue_name]}'" if params[:queue_name].present?
|
145
|
+
result += "AND args #>> '{0, job_class}' = ('#{params[:job_name]}')" if params[:job_name].present?
|
146
|
+
result
|
147
|
+
end
|
148
|
+
|
149
|
+
def fetch_jobs_sql(per_page, offset, where_condition)
|
94
150
|
<<-SQL.squish
|
95
151
|
SELECT que_jobs.*
|
96
152
|
FROM que_jobs
|
@@ -99,12 +155,7 @@ module Que
|
|
99
155
|
FROM pg_locks
|
100
156
|
WHERE locktype = 'advisory'
|
101
157
|
) locks ON (que_jobs.id=locks.job_id)
|
102
|
-
|
103
|
-
AND error_count = 0
|
104
|
-
AND (
|
105
|
-
job_class ILIKE ('#{search}')
|
106
|
-
OR que_jobs.args #>> '{0, job_class}' ILIKE ('#{search}')
|
107
|
-
)
|
158
|
+
#{where_condition}
|
108
159
|
ORDER BY run_at, id
|
109
160
|
LIMIT #{per_page}::int
|
110
161
|
OFFSET #{offset}::int
|
data/lib/que/view/version.rb
CHANGED
data/lib/que/view.rb
CHANGED
@@ -35,7 +35,9 @@ module Que
|
|
35
35
|
|
36
36
|
# Public: All the methods delegated to instance. These should match the interface of Que::View::DSL.
|
37
37
|
def_delegators :instance, :fetch_dashboard_stats,
|
38
|
-
:
|
38
|
+
:fetch_queue_names, :fetch_job_names,
|
39
|
+
:fetch_running_jobs, :fetch_failing_jobs, :fetch_scheduled_jobs, :fetch_finished_jobs,
|
40
|
+
:fetch_expired_jobs, :fetch_job,
|
39
41
|
:delete_failing_jobs, :delete_scheduled_jobs, :delete_job,
|
40
42
|
:reschedule_scheduled_jobs, :reschedule_failing_jobs, :reschedule_job
|
41
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: que-view
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bogdanov Anton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: que
|