que-view 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|