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 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