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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a54cde016dccd6207b38924f717a5aeb3ea9ccae0eef215f848c2621c8af4945
4
- data.tar.gz: 0d44f6c8e723e8533c8f640b58996a2b25b7d444115c761e95879c6a91ca3331
3
+ metadata.gz: d07972f8adf809f3f10f495824376c64724e1526e7afd6d85a05da93632741e0
4
+ data.tar.gz: 95d6ce6e4d1340dcb550a1ae4b85560f19a18de0b9c0dec1eea5ce5a44726ddf
5
5
  SHA512:
6
- metadata.gz: 72bde7eb6e7d6a2ae64e380eedb7f8ce45eb609116a017e71dc1af0ea7acdb5df7293cfb5015474b6d0793570b9062ca735e95878ea1e65e570dd95f0ba4f3d3
7
- data.tar.gz: 659f6cfb80c3bb746f2f0088638e3e6f8d7acd63cc47ab0584df44762a9b18266736fd80bb4820c0a91d0e1b12b3262f7209c7321c7cf867b3ca9f2ee7c7eae9
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
- .row .column {
37
- flex: 1;
36
+ .content {
38
37
  padding: 1rem;
39
38
  }
40
39
 
41
- .row table {
42
- flex: 1;
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
- .actions {
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 a {
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: 200px;
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: 5rem;
110
- line-height: 5rem;
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: .5rem 1rem;
148
- background: #52525b;
149
- color: #fff;
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: #fff;
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: .5rem;
175
+ padding: 1rem;
182
176
  text-decoration: none;
183
- color: #fff;
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
- .navigation .navigation-section ul li.active a {
187
- text-decoration: underline;
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(params[:status])
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 find_job
65
- @job = ::Que::View.fetch_job(params[:id])[0]
66
- return if @job
67
-
68
- redirect_to root_path, notice: 'Job is not found'
69
- end
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(search)[0][status&.to_sym]
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
@@ -4,7 +4,7 @@ module Que
4
4
  module View
5
5
  class WelcomeController < Que::View::ApplicationController
6
6
  def index
7
- @dashboard_stats = ::Que::View.fetch_dashboard_stats('%')[0]
7
+ @dashboard_stats = ::Que::View.fetch_dashboard_stats[0]
8
8
  end
9
9
  end
10
10
  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
- <%= yield %>
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
- <div class="row">
22
- <table cellspacing="0">
23
- <thead>
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
- <th>ID</th>
26
- <th>Run at</th>
27
- <th>Job</th>
28
- <th>Queue</th>
29
- <th>Arguments</th>
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
- <th>Failures</th>
32
- <th>Error</th>
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
- <% job.dig(:args, 0, :arguments).each do |argument| %>
48
- <p><%= argument %></p>
49
- <% end %>
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
- <% if params[:status] == 'failing' %>
52
- <td><%= job[:error_count] %></td>
53
- <td><%= format_error(job) %></td>
54
- <% end %>
55
- <% if %w[failing scheduled].include?(params[:status]) %>
56
- <td>
57
- <div class="actions">
58
- <%= button_to 'Run', job_path(job[:id]), class: 'btn-danger', method: :patch, onclick: "return confirm('Are you sure you wish to reschedule job?')" %>
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="column">
3
- <div class="dashboard-stat running">
4
- <%= link_to jobs_path(status: 'running') do %>
5
- <div class="cell">
6
- <h2>Running</h2>
7
- <span class="dashboard-value">
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="column">
15
- <div class="dashboard-stat scheduled">
16
- <%= link_to jobs_path(status: 'scheduled') do %>
17
- <div class="cell">
18
- <h2>Scheduled</h2>
19
- <span class="dashboard-value">
20
- <%= @dashboard_stats[:scheduled] %>
21
- </span>
22
- </div>
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="column">
27
- <div class="dashboard-stat failing">
28
- <%= link_to jobs_path(status: 'failing') do %>
29
- <div class="cell">
30
- <h2>Failing</h2>
31
- <span class="dashboard-value">
32
- <%= @dashboard_stats[:failing] %>
33
- </span>
34
- </div>
35
- <% end %>
36
- </div>
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(search)
74
+ def fetch_dashboard_stats_sql
55
75
  <<-SQL.squish
56
- SELECT count(*) AS total,
57
- count(locks.job_id) AS running,
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 fetch_failing_jobs_sql(per_page, offset, search)
91
+ def fetch_queue_names_sql
73
92
  <<-SQL.squish
74
- SELECT que_jobs.*
93
+ SELECT COUNT(*) AS count_all, queue AS queue_name
75
94
  FROM que_jobs
76
- LEFT JOIN (
77
- SELECT (classid::bigint << 32) + objid::bigint AS job_id
78
- FROM pg_locks
79
- WHERE locktype = 'advisory'
80
- ) locks ON (que_jobs.id=locks.job_id)
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
- AND (
84
- job_class ILIKE ('#{search}')
85
- OR que_jobs.args #>> '{0, job_class}' ILIKE ('#{search}')
86
- )
87
- ORDER BY run_at, id
88
- LIMIT #{per_page}::int
89
- OFFSET #{offset}::int
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 fetch_scheduled_jobs_sql(per_page, offset, search)
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
- WHERE locks.job_id IS NULL
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Que
4
4
  module View
5
- VERSION = '0.3.0'
5
+ VERSION = '0.3.1'
6
6
  end
7
7
  end
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
- :fetch_running_jobs, :fetch_failing_jobs, :fetch_scheduled_jobs, :fetch_job,
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.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: 2024-02-21 00:00:00.000000000 Z
11
+ date: 2024-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: que