que-view 0.2.3 → 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 +10 -9
- data/app/assets/stylesheets/que/view/application.css +166 -49
- data/app/controllers/que/view/jobs_controller.rb +42 -21
- data/app/controllers/que/view/welcome_controller.rb +1 -1
- data/app/views/layouts/que/view/application.html.erb +30 -1
- data/app/views/que/view/jobs/index.html.erb +70 -43
- data/app/views/que/view/jobs/show.html.erb +1 -1
- data/app/views/que/view/welcome/index.html.erb +55 -33
- data/lib/que/view/dsl.rb +85 -30
- data/lib/que/view/pagination.rb +49 -0
- data/lib/que/view/version.rb +1 -1
- data/lib/que/view.rb +7 -4
- metadata +3 -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
@@ -1,7 +1,16 @@
|
|
1
1
|
# Que::View
|
2
2
|
Rails engine inspired by [Que::Web](https://github.com/statianzo/que-web) for [Que](https://github.com/que-rb/que) job queue.
|
3
3
|
SQL queries came from Que::Web, some styling from there too.
|
4
|
-
|
4
|
+
|
5
|
+
Benefits for using this one:
|
6
|
+
- no Sinatra (que-web based on Sinatra),
|
7
|
+
- no Foundation for styles,
|
8
|
+
- no jQuery,
|
9
|
+
- Que::Web was last updated 2 years ago.
|
10
|
+
|
11
|
+
<img width="1735" alt="Снимок экрана 2024-02-21 в 15 10 35" src="https://github.com/kortirso/que-view/assets/6195394/cd2812c7-abb0-48d9-92d5-4dbef93bcd9e">
|
12
|
+
<img width="1735" alt="Снимок экрана 2024-02-21 в 15 11 12" src="https://github.com/kortirso/que-view/assets/6195394/8af01e7f-a002-4ef1-aeff-f96fd27c639f">
|
13
|
+
|
5
14
|
|
6
15
|
## Installation
|
7
16
|
|
@@ -44,13 +53,5 @@ Add this line to assets/config/manifest.js
|
|
44
53
|
//= link que/view/application.css
|
45
54
|
```
|
46
55
|
|
47
|
-
## TODO
|
48
|
-
|
49
|
-
- [X] rescheduling jobs
|
50
|
-
- [X] deleting jobs
|
51
|
-
- [ ] better styles for UI
|
52
|
-
- [ ] rendering running jobs
|
53
|
-
- [ ] tests
|
54
|
-
|
55
56
|
## License
|
56
57
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -20,67 +20,64 @@
|
|
20
20
|
font-family: 'Source Sans Pro';
|
21
21
|
}
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
margin: 0 auto;
|
27
|
-
max-width: 75rem;
|
28
|
-
width: 100%;
|
29
|
-
}
|
30
|
-
|
31
|
-
.row .column {
|
32
|
-
flex: 1;
|
33
|
-
padding: 1rem;
|
34
|
-
}
|
35
|
-
|
36
|
-
.row table {
|
37
|
-
flex: 1;
|
38
|
-
margin-bottom: 1rem;
|
39
|
-
}
|
40
|
-
|
41
|
-
table th, table td {
|
42
|
-
padding: .25rem .75rem;
|
43
|
-
border-bottom: 1px solid #BBB;
|
23
|
+
html, body {
|
24
|
+
margin: 0;
|
25
|
+
padding: 0;
|
44
26
|
}
|
45
27
|
|
46
|
-
|
47
|
-
|
28
|
+
body {
|
29
|
+
background: #fafafa;
|
48
30
|
}
|
49
31
|
|
50
|
-
|
51
|
-
|
32
|
+
.row {
|
33
|
+
display: flex;
|
52
34
|
}
|
53
35
|
|
54
|
-
|
55
|
-
|
36
|
+
.content {
|
37
|
+
padding: 1rem;
|
56
38
|
}
|
57
39
|
|
58
|
-
|
59
|
-
|
40
|
+
.actions {
|
41
|
+
display: flex;
|
60
42
|
}
|
61
43
|
|
62
|
-
.
|
44
|
+
.dashboard-row {
|
63
45
|
display: flex;
|
46
|
+
margin: 0 auto;
|
47
|
+
width: 100%;
|
48
|
+
border: 1px solid #a8a29e;
|
49
|
+
border-radius: .25rem;
|
50
|
+
overflow: hidden;
|
64
51
|
}
|
65
52
|
|
66
|
-
.dashboard-stat {
|
53
|
+
.dashboard-row .dashboard-stat {
|
67
54
|
text-align: center;
|
68
55
|
display: table;
|
69
56
|
width: 100%;
|
57
|
+
background: #fff;
|
58
|
+
border-right: 1px solid #a8a29e;
|
59
|
+
}
|
60
|
+
|
61
|
+
.dashboard-row .dashboard-stat:hover {
|
62
|
+
background: #f5f5f4;
|
70
63
|
}
|
71
64
|
|
72
|
-
.dashboard-stat
|
65
|
+
.dashboard-row .dashboard-stat:nth-last-of-type(1) {
|
66
|
+
border: none;
|
67
|
+
}
|
68
|
+
|
69
|
+
.dashboard-row .dashboard-stat a {
|
73
70
|
text-decoration: none;
|
74
71
|
}
|
75
72
|
|
76
|
-
.dashboard-stat .cell {
|
73
|
+
.dashboard-row .dashboard-stat .cell {
|
77
74
|
display: flex;
|
78
75
|
flex-direction: column;
|
79
76
|
justify-content: center;
|
80
|
-
height:
|
77
|
+
height: 150px;
|
81
78
|
}
|
82
79
|
|
83
|
-
.dashboard-stat h2 {
|
80
|
+
.dashboard-row .dashboard-stat h2 {
|
84
81
|
color: #222;
|
85
82
|
font-size: 1rem;
|
86
83
|
font-weight: normal;
|
@@ -88,24 +85,23 @@ table p {
|
|
88
85
|
margin: 0;
|
89
86
|
}
|
90
87
|
|
91
|
-
.dashboard-stat.running {
|
92
|
-
background: #CFD0C1;
|
93
|
-
}
|
94
|
-
|
95
|
-
.dashboard-stat.scheduled {
|
96
|
-
background: #828E8C;
|
97
|
-
}
|
98
|
-
|
99
|
-
.dashboard-stat.failing {
|
100
|
-
background: #E8866C;
|
101
|
-
}
|
102
|
-
|
103
88
|
.dashboard-value {
|
104
|
-
font-size:
|
105
|
-
line-height:
|
89
|
+
font-size: 2rem;
|
90
|
+
line-height: 2rem;
|
106
91
|
color: black;
|
107
92
|
}
|
108
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
|
+
|
109
105
|
.btn-danger {
|
110
106
|
cursor: pointer;
|
111
107
|
border: none;
|
@@ -114,3 +110,124 @@ table p {
|
|
114
110
|
padding: .25rem .5rem;
|
115
111
|
margin-right: .5rem;
|
116
112
|
}
|
113
|
+
|
114
|
+
.pagination {
|
115
|
+
margin: 1rem auto;
|
116
|
+
}
|
117
|
+
|
118
|
+
.pagination .pagination-link {
|
119
|
+
margin: 0 .5rem 0 0;
|
120
|
+
padding: .25rem .5rem;
|
121
|
+
background: #fde68a;
|
122
|
+
border: 1px solid #fcd34d;
|
123
|
+
border-radius: .25rem;
|
124
|
+
text-decoration: none;
|
125
|
+
}
|
126
|
+
|
127
|
+
.pagination .pagination-link.disabled {
|
128
|
+
background: #fef3c7;
|
129
|
+
}
|
130
|
+
|
131
|
+
.pagination .total-pages {
|
132
|
+
margin: 0 .5rem 0 0;
|
133
|
+
padding: .25rem .5rem;
|
134
|
+
}
|
135
|
+
|
136
|
+
.navigation {
|
137
|
+
display: flex;
|
138
|
+
padding: 0 1rem;
|
139
|
+
background: #fff;
|
140
|
+
border-bottom: 1px solid #e7e5e4;
|
141
|
+
color: #000;
|
142
|
+
}
|
143
|
+
|
144
|
+
.navigation h1 {
|
145
|
+
margin: 0 2rem 0 0;
|
146
|
+
display: flex;
|
147
|
+
align-items: center;
|
148
|
+
}
|
149
|
+
|
150
|
+
.navigation h1 a {
|
151
|
+
text-decoration: none;
|
152
|
+
color: #000;
|
153
|
+
}
|
154
|
+
|
155
|
+
.navigation .navigation-section {
|
156
|
+
flex: 1;
|
157
|
+
display: flex;
|
158
|
+
justify-content: space-between;
|
159
|
+
align-items: center;
|
160
|
+
}
|
161
|
+
|
162
|
+
.navigation .navigation-section ul {
|
163
|
+
margin: 0;
|
164
|
+
padding: 0;
|
165
|
+
list-style: none;
|
166
|
+
display: flex;
|
167
|
+
}
|
168
|
+
|
169
|
+
.navigation .navigation-section ul.version li {
|
170
|
+
margin-left: 1rem;
|
171
|
+
}
|
172
|
+
|
173
|
+
.navigation .navigation-section ul li a {
|
174
|
+
display: inline-block;
|
175
|
+
padding: 1rem;
|
176
|
+
text-decoration: none;
|
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;
|
204
|
+
}
|
205
|
+
|
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;
|
222
|
+
}
|
223
|
+
|
224
|
+
@media screen and (max-width: 768px) {
|
225
|
+
.dashboard-row {
|
226
|
+
flex-direction: column;
|
227
|
+
}
|
228
|
+
|
229
|
+
.dashboard-row .dashboard-stat {
|
230
|
+
border-right: none;
|
231
|
+
border-bottom: 1px solid #a8a29e;
|
232
|
+
}
|
233
|
+
}
|
@@ -5,11 +5,13 @@ 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(
|
12
|
-
|
13
|
+
@jobs = find_jobs(index_params)
|
14
|
+
paginate
|
13
15
|
end
|
14
16
|
|
15
17
|
def show; end
|
@@ -48,6 +50,18 @@ 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
|
+
|
51
65
|
def find_job
|
52
66
|
@job = ::Que::View.fetch_job(params[:id])[0]
|
53
67
|
return if @job
|
@@ -55,16 +69,32 @@ module Que
|
|
55
69
|
redirect_to root_path, notice: 'Job is not found'
|
56
70
|
end
|
57
71
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
72
|
+
def paginate
|
73
|
+
return if %w[failing scheduled].exclude?(params[:status])
|
74
|
+
return unless @jobs.any?
|
75
|
+
|
76
|
+
@pagination = Que::View::Pagination.new(
|
77
|
+
params: {
|
78
|
+
page: page,
|
79
|
+
per_page: params[:per_page] || PER_PAGE,
|
80
|
+
count: find_jobs_total_amount(params[:status])
|
81
|
+
}
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
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)
|
62
92
|
else []
|
63
93
|
end
|
64
94
|
end
|
65
95
|
|
66
|
-
def
|
67
|
-
::Que::View.fetch_dashboard_stats
|
96
|
+
def find_jobs_total_amount(status)
|
97
|
+
::Que::View.fetch_dashboard_stats[0][status&.to_sym]
|
68
98
|
end
|
69
99
|
|
70
100
|
def reschedule_all_jobs(status, time)
|
@@ -83,25 +113,16 @@ module Que
|
|
83
113
|
end
|
84
114
|
end
|
85
115
|
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
"%#{search_param}%"
|
90
|
-
end
|
91
|
-
|
92
|
-
def search_param
|
93
|
-
sanitised = (params[:search] || '').gsub(/[^0-9a-z:]/i, '')
|
94
|
-
return if sanitised.empty?
|
95
|
-
|
96
|
-
sanitised
|
116
|
+
def offset
|
117
|
+
(page - 1) * PER_PAGE
|
97
118
|
end
|
98
119
|
|
99
120
|
def page
|
100
121
|
(params[:page] || 1).to_i
|
101
122
|
end
|
102
123
|
|
103
|
-
def
|
104
|
-
(
|
124
|
+
def index_params
|
125
|
+
params.permit(:status, :queue_name, :job_name).to_h.symbolize_keys
|
105
126
|
end
|
106
127
|
end
|
107
128
|
end
|
@@ -7,6 +7,35 @@
|
|
7
7
|
<%= stylesheet_link_tag 'que/view/application', media: 'all' %>
|
8
8
|
</head>
|
9
9
|
<body>
|
10
|
-
|
10
|
+
<nav class="navigation" role="navigation">
|
11
|
+
<h1><%= link_to 'Que View', root_path %></h1>
|
12
|
+
<section class="navigation-section">
|
13
|
+
<ul>
|
14
|
+
<li class="<%= 'active' if params[:status] == 'scheduled' %>">
|
15
|
+
<%= link_to 'Scheduled', jobs_path(status: 'scheduled') %>
|
16
|
+
</li>
|
17
|
+
<li class="<%= 'active' if params[:status] == 'failing' %>">
|
18
|
+
<%= link_to 'Failing', jobs_path(status: 'failing') %>
|
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>
|
29
|
+
</ul>
|
30
|
+
<ul class="version">
|
31
|
+
<li>Que <%= Que::VERSION %></li>
|
32
|
+
<li>Que View <%= Que::View::VERSION %></li>
|
33
|
+
<li><%= Time.now.utc.strftime("%Y-%m-%d %H:%M:%S") %></li>
|
34
|
+
</ul>
|
35
|
+
</section>
|
36
|
+
</nav>
|
37
|
+
<section class="content">
|
38
|
+
<%= yield %>
|
39
|
+
</section>
|
11
40
|
</body>
|
12
41
|
</html>
|
@@ -1,55 +1,82 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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 %>
|
9
|
+
<% if @jobs.any? %>
|
10
|
+
<% if @pagination %>
|
11
|
+
<div class="row pagination">
|
12
|
+
<% if @pagination.previous_page? %>
|
13
|
+
<%= link_to 'First', jobs_path(status: params[:status], page: 1), class: 'pagination-link' %>
|
14
|
+
<%= link_to '< Previous', jobs_path(status: params[:status], page: @pagination.previous_page), class: 'pagination-link' %>
|
15
|
+
<% else %>
|
16
|
+
<p class="pagination-link disabled">First</p>
|
17
|
+
<p class="pagination-link disabled">< Previous</p>
|
18
|
+
<% end %>
|
19
|
+
<p class="total-pages"><%= "Page #{@pagination.page} of #{@pagination.total_pages}" %></p>
|
20
|
+
<% if @pagination.next_page? %>
|
21
|
+
<%= link_to 'Next >', jobs_path(status: params[:status], page: @pagination.next_page), class: 'pagination-link' %>
|
22
|
+
<%= link_to 'Last', jobs_path(status: params[:status], page: @pagination.total_pages), class: 'pagination-link' %>
|
23
|
+
<% else %>
|
24
|
+
<p class="pagination-link disabled">Next ></p>
|
25
|
+
<p class="pagination-link disabled">Last </p>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
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| %>
|
5
48
|
<tr>
|
6
|
-
<
|
7
|
-
<
|
8
|
-
<
|
9
|
-
<
|
10
|
-
<
|
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>
|
11
58
|
<% if params[:status] == 'failing' %>
|
12
|
-
<
|
13
|
-
<
|
59
|
+
<td><%= job[:error_count] %></td>
|
60
|
+
<td><%= format_error(job) %></td>
|
14
61
|
<% end %>
|
15
62
|
<% if %w[failing scheduled].include?(params[:status]) %>
|
16
|
-
<th></th>
|
17
|
-
<% end %>
|
18
|
-
</tr>
|
19
|
-
</thead>
|
20
|
-
<tbody>
|
21
|
-
<% @jobs.each do |job| %>
|
22
|
-
<tr>
|
23
|
-
<td><%= link_to job[:id], job_path(job[:id]) %></td>
|
24
|
-
<td><%= job[:run_at].utc %></td>
|
25
|
-
<td><%= humanized_job_class(job) %></td>
|
26
|
-
<td><%= job[:queue] %></td>
|
27
63
|
<td>
|
28
|
-
|
29
|
-
<p><%= argument %></p>
|
30
|
-
<% end %>
|
31
|
-
</td>
|
32
|
-
<% if params[:status] == 'failing' %>
|
33
|
-
<td><%= job[:error_count] %></td>
|
34
|
-
<td><%= format_error(job) %></td>
|
35
|
-
<% end %>
|
36
|
-
<% if %w[failing scheduled].include?(params[:status]) %>
|
37
|
-
<td class="actions">
|
64
|
+
<div class="actions">
|
38
65
|
<%= button_to 'Run', job_path(job[:id]), class: 'btn-danger', method: :patch, onclick: "return confirm('Are you sure you wish to reschedule job?')" %>
|
39
66
|
<%= button_to 'Delete', job_path(job[:id]), class: 'btn-danger', method: :delete, onclick: "return confirm('Are you sure you wish to delete job?')" %>
|
40
|
-
</
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
</
|
46
|
-
|
47
|
-
<p>No jobs found</p>
|
48
|
-
<% end %>
|
49
|
-
</div>
|
50
|
-
<% if %w[failing scheduled].include?(params[:status]) && @jobs_amount.positive? %>
|
67
|
+
</div>
|
68
|
+
</td>
|
69
|
+
<% end %>
|
70
|
+
</tr>
|
71
|
+
<% end %>
|
72
|
+
</tbody>
|
73
|
+
</table>
|
51
74
|
<div class="row">
|
52
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?')" %>
|
53
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?')" %>
|
54
77
|
</div>
|
78
|
+
<% else %>
|
79
|
+
<div class="row">
|
80
|
+
<p>No jobs found</p>
|
81
|
+
</div>
|
55
82
|
<% 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,24 @@ 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
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch_running_jobs(...)
|
24
|
+
Que.job_states
|
9
25
|
end
|
10
26
|
|
11
27
|
def fetch_failing_jobs(...)
|
@@ -16,6 +32,14 @@ module Que
|
|
16
32
|
execute(fetch_scheduled_jobs_sql(...))
|
17
33
|
end
|
18
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
|
+
|
19
43
|
def fetch_job(...)
|
20
44
|
execute(fetch_job_sql(...))
|
21
45
|
end
|
@@ -47,46 +71,82 @@ module Que
|
|
47
71
|
private
|
48
72
|
|
49
73
|
# rubocop: disable Metrics/MethodLength
|
50
|
-
def fetch_dashboard_stats_sql
|
74
|
+
def fetch_dashboard_stats_sql
|
51
75
|
<<-SQL.squish
|
52
|
-
SELECT count(*)
|
53
|
-
count(locks.job_id)
|
76
|
+
SELECT count(*) AS total,
|
77
|
+
count(locks.job_id) AS running,
|
54
78
|
coalesce(sum((error_count > 0 AND locks.job_id IS NULL)::int), 0) AS failing,
|
55
|
-
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
|
56
82
|
FROM que_jobs
|
57
83
|
LEFT JOIN (
|
58
84
|
SELECT (classid::bigint << 32) + objid::bigint AS job_id
|
59
85
|
FROM pg_locks
|
60
86
|
WHERE locktype = 'advisory'
|
61
87
|
) locks ON (que_jobs.id=locks.job_id)
|
62
|
-
WHERE
|
63
|
-
job_class ILIKE ('#{search}')
|
64
|
-
OR que_jobs.args #>> '{0, job_class}' ILIKE ('#{search}')
|
65
88
|
SQL
|
66
89
|
end
|
67
90
|
|
68
|
-
def
|
91
|
+
def fetch_queue_names_sql
|
69
92
|
<<-SQL.squish
|
70
|
-
SELECT
|
93
|
+
SELECT COUNT(*) AS count_all, queue AS queue_name
|
71
94
|
FROM que_jobs
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
77
110
|
WHERE locks.job_id IS NULL
|
78
111
|
AND error_count > 0
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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)}
|
86
122
|
SQL
|
123
|
+
fetch_jobs_sql(per_page, offset, where_condition)
|
87
124
|
end
|
88
125
|
|
89
|
-
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)
|
90
150
|
<<-SQL.squish
|
91
151
|
SELECT que_jobs.*
|
92
152
|
FROM que_jobs
|
@@ -95,12 +155,7 @@ module Que
|
|
95
155
|
FROM pg_locks
|
96
156
|
WHERE locktype = 'advisory'
|
97
157
|
) locks ON (que_jobs.id=locks.job_id)
|
98
|
-
|
99
|
-
AND error_count = 0
|
100
|
-
AND (
|
101
|
-
job_class ILIKE ('#{search}')
|
102
|
-
OR que_jobs.args #>> '{0, job_class}' ILIKE ('#{search}')
|
103
|
-
)
|
158
|
+
#{where_condition}
|
104
159
|
ORDER BY run_at, id
|
105
160
|
LIMIT #{per_page}::int
|
106
161
|
OFFSET #{offset}::int
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Que
|
4
|
+
module View
|
5
|
+
class Pagination
|
6
|
+
attr_reader :page, :count, :per_page
|
7
|
+
|
8
|
+
def initialize(params: {})
|
9
|
+
@page = params[:page]
|
10
|
+
@count = params[:count]
|
11
|
+
@per_page = params[:per_page]
|
12
|
+
end
|
13
|
+
|
14
|
+
def offset
|
15
|
+
return 0 if page == 1
|
16
|
+
|
17
|
+
per_page * (page.to_i - 1)
|
18
|
+
end
|
19
|
+
|
20
|
+
def next_page
|
21
|
+
page + 1 unless last_page?
|
22
|
+
end
|
23
|
+
|
24
|
+
def next_page?
|
25
|
+
page < total_pages
|
26
|
+
end
|
27
|
+
|
28
|
+
def previous_page
|
29
|
+
page - 1 unless first_page?
|
30
|
+
end
|
31
|
+
|
32
|
+
def previous_page?
|
33
|
+
page > 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def last_page?
|
37
|
+
page == total_pages
|
38
|
+
end
|
39
|
+
|
40
|
+
def first_page?
|
41
|
+
page == 1
|
42
|
+
end
|
43
|
+
|
44
|
+
def total_pages
|
45
|
+
(count / per_page.to_f).ceil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/que/view/version.rb
CHANGED
data/lib/que/view.rb
CHANGED
@@ -6,13 +6,14 @@ require 'que/view/version'
|
|
6
6
|
require 'que/view/engine'
|
7
7
|
require 'que/view/configuration'
|
8
8
|
require 'que/view/dsl'
|
9
|
+
require 'que/view/pagination'
|
9
10
|
|
10
11
|
module Que
|
11
12
|
module View
|
12
13
|
extend self
|
13
14
|
extend Forwardable
|
14
15
|
|
15
|
-
# Public: Configure
|
16
|
+
# Public: Configure que view.
|
16
17
|
#
|
17
18
|
# Que::View.configure do |config|
|
18
19
|
# end
|
@@ -26,15 +27,17 @@ module Que
|
|
26
27
|
@configuration ||= Configuration.new
|
27
28
|
end
|
28
29
|
|
29
|
-
# Public: Default per thread
|
30
|
+
# Public: Default per thread que view instance if configured.
|
30
31
|
# Returns Que::View::DSL instance.
|
31
32
|
def instance
|
32
33
|
Thread.current[:que_view_instance] ||= DSL.new
|
33
34
|
end
|
34
35
|
|
35
36
|
# Public: All the methods delegated to instance. These should match the interface of Que::View::DSL.
|
36
|
-
def_delegators :instance,
|
37
|
-
:
|
37
|
+
def_delegators :instance, :fetch_dashboard_stats,
|
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,
|
38
41
|
:delete_failing_jobs, :delete_scheduled_jobs, :delete_job,
|
39
42
|
:reschedule_scheduled_jobs, :reschedule_failing_jobs, :reschedule_job
|
40
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.
|
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:
|
11
|
+
date: 2024-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: que
|
@@ -175,6 +175,7 @@ files:
|
|
175
175
|
- lib/que/view/configuration.rb
|
176
176
|
- lib/que/view/dsl.rb
|
177
177
|
- lib/que/view/engine.rb
|
178
|
+
- lib/que/view/pagination.rb
|
178
179
|
- lib/que/view/version.rb
|
179
180
|
homepage: https://github.com/kortirso/que-view
|
180
181
|
licenses:
|