solid_queue_tui 0.1.1 → 0.1.3
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/exe/sqtui +37 -1
- data/lib/solid_queue_tui/actions/discard_job.rb +4 -21
- data/lib/solid_queue_tui/actions/discard_scheduled_job.rb +5 -21
- data/lib/solid_queue_tui/actions/dispatch_scheduled_job.rb +4 -23
- data/lib/solid_queue_tui/actions/enqueue_recurring_task.rb +12 -0
- data/lib/solid_queue_tui/actions/retry_job.rb +14 -59
- data/lib/solid_queue_tui/actions/toggle_queue_pause.rb +19 -0
- data/lib/solid_queue_tui/application.rb +109 -21
- data/lib/solid_queue_tui/cli.rb +15 -8
- data/lib/solid_queue_tui/components/header.rb +26 -27
- data/lib/solid_queue_tui/components/help_bar.rb +1 -0
- data/lib/solid_queue_tui/components/job_table.rb +13 -2
- data/lib/solid_queue_tui/data/failed_query.rb +37 -91
- data/lib/solid_queue_tui/data/jobs_query.rb +119 -121
- data/lib/solid_queue_tui/data/processes_query.rb +32 -33
- data/lib/solid_queue_tui/data/queues_query.rb +6 -15
- data/lib/solid_queue_tui/data/recurring_tasks_query.rb +36 -0
- data/lib/solid_queue_tui/data/stats.rb +9 -27
- data/lib/solid_queue_tui/formatting_helpers.rb +63 -0
- data/lib/solid_queue_tui/railtie.rb +9 -0
- data/lib/solid_queue_tui/version.rb +1 -1
- data/lib/solid_queue_tui/views/blocked_view.rb +85 -74
- data/lib/solid_queue_tui/views/concerns/confirmable.rb +53 -0
- data/lib/solid_queue_tui/views/concerns/filterable.rb +128 -0
- data/lib/solid_queue_tui/views/concerns/paginatable.rb +79 -0
- data/lib/solid_queue_tui/views/dashboard_view.rb +4 -5
- data/lib/solid_queue_tui/views/failed_view.rb +65 -179
- data/lib/solid_queue_tui/views/finished_view.rb +33 -114
- data/lib/solid_queue_tui/views/in_progress_view.rb +85 -69
- data/lib/solid_queue_tui/views/job_detail_view.rb +179 -31
- data/lib/solid_queue_tui/views/processes_view.rb +2 -24
- data/lib/solid_queue_tui/views/queues_view.rb +250 -30
- data/lib/solid_queue_tui/views/recurring_tasks_view.rb +155 -0
- data/lib/solid_queue_tui/views/scheduled_view.rb +69 -107
- data/lib/solid_queue_tui.rb +18 -4
- data/lib/tasks/solid_queue_tui.rake +8 -0
- metadata +20 -25
- data/lib/solid_queue_tui/connection.rb +0 -58
|
@@ -3,80 +3,61 @@
|
|
|
3
3
|
module SolidQueueTui
|
|
4
4
|
module Views
|
|
5
5
|
class FailedView
|
|
6
|
+
include Filterable
|
|
7
|
+
include Confirmable
|
|
8
|
+
include Paginatable
|
|
9
|
+
include FormattingHelpers
|
|
10
|
+
|
|
6
11
|
def initialize(tui)
|
|
7
12
|
@tui = tui
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@failed_jobs = []
|
|
12
|
-
@filter = nil
|
|
13
|
-
@filter_mode = false
|
|
14
|
-
@filter_input = ""
|
|
15
|
-
@confirm_action = nil
|
|
13
|
+
init_pagination
|
|
14
|
+
init_confirm
|
|
15
|
+
init_filter
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def update(failed_jobs:)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
update_items(failed_jobs)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def append(failed_jobs:)
|
|
23
|
+
append_items(failed_jobs)
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
def render(frame, area)
|
|
25
|
-
if
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@tui.constraint_fill(1),
|
|
31
|
-
@tui.constraint_length(3)
|
|
32
|
-
]
|
|
33
|
-
)
|
|
34
|
-
render_failed_table(frame, content_area)
|
|
35
|
-
render_confirm(frame, confirm_area)
|
|
36
|
-
elsif @filter_mode
|
|
37
|
-
content_area, filter_area = @tui.layout_split(
|
|
27
|
+
if confirm_mode?
|
|
28
|
+
render_failed_table(frame, area)
|
|
29
|
+
render_confirm_popup(frame, area)
|
|
30
|
+
elsif filter_mode?
|
|
31
|
+
filter_area, content_area = @tui.layout_split(
|
|
38
32
|
area,
|
|
39
33
|
direction: :vertical,
|
|
40
34
|
constraints: [
|
|
41
|
-
@tui.
|
|
42
|
-
@tui.
|
|
35
|
+
@tui.constraint_length(3),
|
|
36
|
+
@tui.constraint_fill(1)
|
|
43
37
|
]
|
|
44
38
|
)
|
|
45
|
-
render_failed_table(frame, content_area)
|
|
46
39
|
render_filter_input(frame, filter_area)
|
|
40
|
+
render_failed_table(frame, content_area)
|
|
47
41
|
else
|
|
48
42
|
render_failed_table(frame, area)
|
|
49
43
|
end
|
|
50
44
|
end
|
|
51
45
|
|
|
52
46
|
def handle_input(event)
|
|
53
|
-
if
|
|
47
|
+
if confirm_mode?
|
|
54
48
|
handle_confirm_input(event)
|
|
55
|
-
elsif
|
|
49
|
+
elsif filter_mode?
|
|
56
50
|
handle_filter_input(event)
|
|
57
51
|
else
|
|
58
52
|
handle_normal_input(event)
|
|
59
53
|
end
|
|
60
54
|
end
|
|
61
55
|
|
|
62
|
-
def selected_item
|
|
63
|
-
return nil if @failed_jobs.empty? || @selected_row >= @failed_jobs.size
|
|
64
|
-
@failed_jobs[@selected_row]
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def filter = @filter
|
|
68
|
-
|
|
69
56
|
def bindings
|
|
70
|
-
if
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
]
|
|
75
|
-
elsif @filter_mode
|
|
76
|
-
[
|
|
77
|
-
{ key: "Enter", action: "Apply" },
|
|
78
|
-
{ key: "Esc", action: "Cancel" }
|
|
79
|
-
]
|
|
57
|
+
if confirm_mode?
|
|
58
|
+
confirm_bindings
|
|
59
|
+
elsif filter_mode?
|
|
60
|
+
filter_bindings
|
|
80
61
|
else
|
|
81
62
|
[
|
|
82
63
|
{ key: "j/k", action: "Navigate" },
|
|
@@ -90,11 +71,11 @@ module SolidQueueTui
|
|
|
90
71
|
end
|
|
91
72
|
|
|
92
73
|
def capturing_input?
|
|
93
|
-
|
|
74
|
+
filter_mode? || confirm_mode?
|
|
94
75
|
end
|
|
95
76
|
|
|
96
77
|
def breadcrumb
|
|
97
|
-
@
|
|
78
|
+
@filters.empty? ? "failed" : "failed:filtered"
|
|
98
79
|
end
|
|
99
80
|
|
|
100
81
|
private
|
|
@@ -104,7 +85,9 @@ module SolidQueueTui
|
|
|
104
85
|
in { type: :key, code: "j" } | { type: :key, code: "up" }
|
|
105
86
|
move_selection(-1)
|
|
106
87
|
in { type: :key, code: "k" } | { type: :key, code: "down" }
|
|
107
|
-
move_selection(1)
|
|
88
|
+
result = move_selection(1)
|
|
89
|
+
return :load_more if result == :load_more
|
|
90
|
+
nil
|
|
108
91
|
in { type: :key, code: "g" }
|
|
109
92
|
jump_to_top
|
|
110
93
|
in { type: :key, code: "G" }
|
|
@@ -116,88 +99,52 @@ module SolidQueueTui
|
|
|
116
99
|
@confirm_action = :discard if selected_item
|
|
117
100
|
nil
|
|
118
101
|
in { type: :key, code: "A" }
|
|
119
|
-
@confirm_action = :retry_all unless
|
|
102
|
+
@confirm_action = :retry_all unless items.empty?
|
|
120
103
|
nil
|
|
121
104
|
in { type: :key, code: "/" }
|
|
122
|
-
|
|
123
|
-
@filter_input = @filter || ""
|
|
105
|
+
enter_filter_mode
|
|
124
106
|
nil
|
|
125
107
|
in { type: :key, code: "esc" }
|
|
126
|
-
|
|
127
|
-
@filter_input = ""
|
|
128
|
-
:refresh
|
|
108
|
+
clear_filter
|
|
129
109
|
else
|
|
130
110
|
nil
|
|
131
111
|
end
|
|
132
112
|
end
|
|
133
113
|
|
|
134
|
-
def
|
|
135
|
-
case
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
item = selected_item
|
|
147
|
-
return nil unless item
|
|
148
|
-
Actions::DiscardJob.call(item.id)
|
|
149
|
-
:refresh
|
|
150
|
-
when :retry_all
|
|
151
|
-
Actions::RetryJob.retry_all
|
|
152
|
-
:refresh
|
|
153
|
-
end
|
|
154
|
-
in { type: :key, code: "n" } | { type: :key, code: "esc" }
|
|
155
|
-
@confirm_action = nil
|
|
156
|
-
nil
|
|
157
|
-
else
|
|
158
|
-
nil
|
|
114
|
+
def confirm_message
|
|
115
|
+
case @confirm_action
|
|
116
|
+
when :retry
|
|
117
|
+
job = selected_item
|
|
118
|
+
"Retry job ##{job&.job_id} (#{job&.class_name})? [y/n]"
|
|
119
|
+
when :discard
|
|
120
|
+
job = selected_item
|
|
121
|
+
"Discard job ##{job&.job_id} (#{job&.class_name})? This cannot be undone. [y/n]"
|
|
122
|
+
when :retry_all
|
|
123
|
+
count = @total_count || items.size
|
|
124
|
+
label = @filters.empty? ? "failed jobs" : "filtered failed jobs"
|
|
125
|
+
"Retry ALL #{count} #{label}? [y/n]"
|
|
159
126
|
end
|
|
160
127
|
end
|
|
161
128
|
|
|
162
|
-
def
|
|
163
|
-
case
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
129
|
+
def execute_confirm_action(action)
|
|
130
|
+
case action
|
|
131
|
+
when :retry
|
|
132
|
+
item = selected_item
|
|
133
|
+
return nil unless item
|
|
134
|
+
Actions::RetryJob.call(item.id)
|
|
135
|
+
:refresh
|
|
136
|
+
when :discard
|
|
137
|
+
item = selected_item
|
|
138
|
+
return nil unless item
|
|
139
|
+
Actions::DiscardJob.call(item.id)
|
|
140
|
+
:refresh
|
|
141
|
+
when :retry_all
|
|
142
|
+
f = filters
|
|
143
|
+
Actions::RetryJob.retry_all(filter: f[:class_name], queue: f[:queue])
|
|
169
144
|
:refresh
|
|
170
|
-
in { type: :key, code: "esc" }
|
|
171
|
-
@filter_mode = false
|
|
172
|
-
nil
|
|
173
|
-
in { type: :key, code: "backspace" }
|
|
174
|
-
@filter_input = @filter_input[0...-1]
|
|
175
|
-
nil
|
|
176
|
-
in { type: :key, code: /\A.\z/ => char }
|
|
177
|
-
@filter_input += char
|
|
178
|
-
nil
|
|
179
|
-
else
|
|
180
|
-
nil
|
|
181
145
|
end
|
|
182
146
|
end
|
|
183
147
|
|
|
184
|
-
def move_selection(delta)
|
|
185
|
-
return if @failed_jobs.empty?
|
|
186
|
-
@selected_row = (@selected_row + delta).clamp(0, @failed_jobs.size - 1)
|
|
187
|
-
@table_state.select(@selected_row)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def jump_to_top
|
|
191
|
-
@selected_row = 0
|
|
192
|
-
@table_state.select(0)
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
def jump_to_bottom
|
|
196
|
-
return if @failed_jobs.empty?
|
|
197
|
-
@selected_row = @failed_jobs.size - 1
|
|
198
|
-
@table_state.select(@selected_row)
|
|
199
|
-
end
|
|
200
|
-
|
|
201
148
|
def render_failed_table(frame, area)
|
|
202
149
|
columns = [
|
|
203
150
|
{ key: :id, label: "ID", width: 8 },
|
|
@@ -208,7 +155,7 @@ module SolidQueueTui
|
|
|
208
155
|
{ key: :failed_at, label: "FAILED", width: 12 }
|
|
209
156
|
]
|
|
210
157
|
|
|
211
|
-
rows =
|
|
158
|
+
rows = items.map do |job|
|
|
212
159
|
{
|
|
213
160
|
id: job.job_id,
|
|
214
161
|
class_name: job.class_name,
|
|
@@ -219,80 +166,19 @@ module SolidQueueTui
|
|
|
219
166
|
}
|
|
220
167
|
end
|
|
221
168
|
|
|
222
|
-
title = @filter ? "Failed Jobs (filter: #{@filter})" : "Failed Jobs"
|
|
223
|
-
|
|
224
169
|
table = Components::JobTable.new(
|
|
225
170
|
@tui,
|
|
226
|
-
title:
|
|
171
|
+
title: filter_title("Failed Jobs"),
|
|
227
172
|
columns: columns,
|
|
228
173
|
rows: rows,
|
|
229
174
|
selected_row: @selected_row,
|
|
175
|
+
total_count: @total_count,
|
|
230
176
|
empty_message: "No failed jobs — everything is running smoothly!"
|
|
231
177
|
)
|
|
232
178
|
|
|
233
179
|
table.render(frame, area, @table_state)
|
|
234
180
|
end
|
|
235
181
|
|
|
236
|
-
def render_confirm(frame, area)
|
|
237
|
-
message = case @confirm_action
|
|
238
|
-
when :retry
|
|
239
|
-
job = selected_item
|
|
240
|
-
"Retry job ##{job&.job_id} (#{job&.class_name})? [y/n]"
|
|
241
|
-
when :discard
|
|
242
|
-
job = selected_item
|
|
243
|
-
"Discard job ##{job&.job_id} (#{job&.class_name})? This cannot be undone. [y/n]"
|
|
244
|
-
when :retry_all
|
|
245
|
-
"Retry ALL #{@failed_jobs.size} failed jobs? [y/n]"
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
frame.render_widget(
|
|
249
|
-
@tui.paragraph(
|
|
250
|
-
text: " #{message}",
|
|
251
|
-
style: @tui.style(fg: :yellow, modifiers: [:bold]),
|
|
252
|
-
block: @tui.block(
|
|
253
|
-
title: " Confirm ",
|
|
254
|
-
title_style: @tui.style(fg: :red, modifiers: [:bold]),
|
|
255
|
-
borders: [:all],
|
|
256
|
-
border_type: :rounded,
|
|
257
|
-
border_style: @tui.style(fg: :red)
|
|
258
|
-
)
|
|
259
|
-
),
|
|
260
|
-
area
|
|
261
|
-
)
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
def render_filter_input(frame, area)
|
|
265
|
-
frame.render_widget(
|
|
266
|
-
@tui.paragraph(
|
|
267
|
-
text: @filter_input + "█",
|
|
268
|
-
style: @tui.style(fg: :white),
|
|
269
|
-
block: @tui.block(
|
|
270
|
-
title: " Filter by class name ",
|
|
271
|
-
title_style: @tui.style(fg: :yellow),
|
|
272
|
-
borders: [:all],
|
|
273
|
-
border_type: :rounded,
|
|
274
|
-
border_style: @tui.style(fg: :cyan)
|
|
275
|
-
)
|
|
276
|
-
),
|
|
277
|
-
area
|
|
278
|
-
)
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def truncate(str, max)
|
|
282
|
-
return "" unless str
|
|
283
|
-
str.length > max ? "#{str[0...max - 3]}..." : str
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
def time_ago(time)
|
|
287
|
-
return "n/a" unless time
|
|
288
|
-
seconds = (Time.now.utc - time).to_i
|
|
289
|
-
case seconds
|
|
290
|
-
when 0..59 then "#{seconds}s ago"
|
|
291
|
-
when 60..3599 then "#{seconds / 60}m ago"
|
|
292
|
-
when 3600..86399 then "#{seconds / 3600}h ago"
|
|
293
|
-
else "#{seconds / 86400}d ago"
|
|
294
|
-
end
|
|
295
|
-
end
|
|
296
182
|
end
|
|
297
183
|
end
|
|
298
184
|
end
|
|
@@ -3,61 +3,52 @@
|
|
|
3
3
|
module SolidQueueTui
|
|
4
4
|
module Views
|
|
5
5
|
class FinishedView
|
|
6
|
+
include Filterable
|
|
7
|
+
include Paginatable
|
|
8
|
+
include FormattingHelpers
|
|
9
|
+
|
|
6
10
|
def initialize(tui)
|
|
7
11
|
@tui = tui
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@selected_row = 0
|
|
11
|
-
@jobs = []
|
|
12
|
-
@filter = nil
|
|
13
|
-
@filter_mode = false
|
|
14
|
-
@filter_input = ""
|
|
12
|
+
init_pagination
|
|
13
|
+
init_filter
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
def update(jobs:)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
update_items(jobs)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def append(jobs:)
|
|
21
|
+
append_items(jobs)
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def render(frame, area)
|
|
24
|
-
if
|
|
25
|
-
|
|
25
|
+
if filter_mode?
|
|
26
|
+
filter_area, content_area = @tui.layout_split(
|
|
26
27
|
area,
|
|
27
28
|
direction: :vertical,
|
|
28
29
|
constraints: [
|
|
29
|
-
@tui.
|
|
30
|
-
@tui.
|
|
30
|
+
@tui.constraint_length(3),
|
|
31
|
+
@tui.constraint_fill(1)
|
|
31
32
|
]
|
|
32
33
|
)
|
|
33
|
-
render_table(frame, content_area)
|
|
34
34
|
render_filter_input(frame, filter_area)
|
|
35
|
+
render_table(frame, content_area)
|
|
35
36
|
else
|
|
36
37
|
render_table(frame, area)
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def handle_input(event)
|
|
41
|
-
if
|
|
42
|
+
if filter_mode?
|
|
42
43
|
handle_filter_input(event)
|
|
43
44
|
else
|
|
44
45
|
handle_normal_input(event)
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
48
|
-
def selected_item
|
|
49
|
-
return nil if @jobs.empty? || @selected_row >= @jobs.size
|
|
50
|
-
@jobs[@selected_row]
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def filter = @filter
|
|
54
|
-
|
|
55
49
|
def bindings
|
|
56
|
-
if
|
|
57
|
-
|
|
58
|
-
{ key: "Enter", action: "Apply" },
|
|
59
|
-
{ key: "Esc", action: "Cancel" }
|
|
60
|
-
]
|
|
50
|
+
if filter_mode?
|
|
51
|
+
filter_bindings
|
|
61
52
|
else
|
|
62
53
|
[
|
|
63
54
|
{ key: "j/k", action: "Navigate" },
|
|
@@ -70,11 +61,11 @@ module SolidQueueTui
|
|
|
70
61
|
end
|
|
71
62
|
|
|
72
63
|
def capturing_input?
|
|
73
|
-
|
|
64
|
+
filter_mode?
|
|
74
65
|
end
|
|
75
66
|
|
|
76
67
|
def breadcrumb
|
|
77
|
-
@
|
|
68
|
+
@filters.empty? ? "finished" : "finished:filtered"
|
|
78
69
|
end
|
|
79
70
|
|
|
80
71
|
private
|
|
@@ -84,63 +75,23 @@ module SolidQueueTui
|
|
|
84
75
|
in { type: :key, code: "j" } | { type: :key, code: "up" }
|
|
85
76
|
move_selection(-1)
|
|
86
77
|
in { type: :key, code: "k" } | { type: :key, code: "down" }
|
|
87
|
-
move_selection(1)
|
|
78
|
+
result = move_selection(1)
|
|
79
|
+
return :load_more if result == :load_more
|
|
80
|
+
nil
|
|
88
81
|
in { type: :key, code: "g" }
|
|
89
82
|
jump_to_top
|
|
90
83
|
in { type: :key, code: "G" }
|
|
91
84
|
jump_to_bottom
|
|
92
85
|
in { type: :key, code: "/" }
|
|
93
|
-
|
|
94
|
-
@filter_input = @filter || ""
|
|
95
|
-
in { type: :key, code: "esc" }
|
|
96
|
-
@filter = nil
|
|
97
|
-
@filter_input = ""
|
|
98
|
-
:refresh
|
|
99
|
-
else
|
|
86
|
+
enter_filter_mode
|
|
100
87
|
nil
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def handle_filter_input(event)
|
|
105
|
-
case event
|
|
106
|
-
in { type: :key, code: "enter" }
|
|
107
|
-
@filter = @filter_input.empty? ? nil : @filter_input
|
|
108
|
-
@filter_mode = false
|
|
109
|
-
@selected_row = 0
|
|
110
|
-
@table_state.select(0)
|
|
111
|
-
:refresh
|
|
112
88
|
in { type: :key, code: "esc" }
|
|
113
|
-
|
|
114
|
-
@filter_input = @filter || ""
|
|
115
|
-
nil
|
|
116
|
-
in { type: :key, code: "backspace" }
|
|
117
|
-
@filter_input = @filter_input[0...-1]
|
|
118
|
-
nil
|
|
119
|
-
in { type: :key, code: /\A.\z/ => char }
|
|
120
|
-
@filter_input += char
|
|
121
|
-
nil
|
|
89
|
+
clear_filter
|
|
122
90
|
else
|
|
123
91
|
nil
|
|
124
92
|
end
|
|
125
93
|
end
|
|
126
94
|
|
|
127
|
-
def move_selection(delta)
|
|
128
|
-
return if @jobs.empty?
|
|
129
|
-
@selected_row = (@selected_row + delta).clamp(0, @jobs.size - 1)
|
|
130
|
-
@table_state.select(@selected_row)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def jump_to_top
|
|
134
|
-
@selected_row = 0
|
|
135
|
-
@table_state.select(0)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def jump_to_bottom
|
|
139
|
-
return if @jobs.empty?
|
|
140
|
-
@selected_row = @jobs.size - 1
|
|
141
|
-
@table_state.select(@selected_row)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
95
|
def render_table(frame, area)
|
|
145
96
|
columns = [
|
|
146
97
|
{ key: :id, label: "ID", width: 8 },
|
|
@@ -151,65 +102,33 @@ module SolidQueueTui
|
|
|
151
102
|
{ key: :duration, label: "DURATION", width: 12 }
|
|
152
103
|
]
|
|
153
104
|
|
|
154
|
-
rows =
|
|
105
|
+
rows = items.map do |job|
|
|
155
106
|
{
|
|
156
107
|
id: job.id,
|
|
157
108
|
queue_name: job.queue_name,
|
|
158
109
|
class_name: job.class_name,
|
|
159
110
|
priority: job.priority,
|
|
160
111
|
finished_at: format_time(job.finished_at),
|
|
161
|
-
duration:
|
|
112
|
+
duration: job_duration(job.created_at, job.finished_at)
|
|
162
113
|
}
|
|
163
114
|
end
|
|
164
115
|
|
|
165
|
-
title = @filter ? "Finished (filter: #{@filter})" : "Finished"
|
|
166
|
-
|
|
167
116
|
table = Components::JobTable.new(
|
|
168
117
|
@tui,
|
|
169
|
-
title:
|
|
118
|
+
title: filter_title("Finished"),
|
|
170
119
|
columns: columns,
|
|
171
120
|
rows: rows,
|
|
172
121
|
selected_row: @selected_row,
|
|
173
|
-
|
|
122
|
+
total_count: @total_count,
|
|
123
|
+
empty_message: @filters.empty? ? "No finished jobs" : "No finished jobs matching filters"
|
|
174
124
|
)
|
|
175
125
|
|
|
176
126
|
table.render(frame, area, @table_state)
|
|
177
127
|
end
|
|
178
128
|
|
|
179
|
-
def
|
|
180
|
-
frame.render_widget(
|
|
181
|
-
@tui.paragraph(
|
|
182
|
-
text: @filter_input + "\u2588",
|
|
183
|
-
style: @tui.style(fg: :white),
|
|
184
|
-
block: @tui.block(
|
|
185
|
-
title: " Filter by class name ",
|
|
186
|
-
title_style: @tui.style(fg: :yellow),
|
|
187
|
-
borders: [:all],
|
|
188
|
-
border_type: :rounded,
|
|
189
|
-
border_style: @tui.style(fg: :cyan)
|
|
190
|
-
)
|
|
191
|
-
),
|
|
192
|
-
area
|
|
193
|
-
)
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
def format_time(time)
|
|
197
|
-
return "n/a" unless time
|
|
198
|
-
time.strftime("%Y-%m-%d %H:%M:%S")
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def format_duration(created, finished)
|
|
129
|
+
def job_duration(created, finished)
|
|
202
130
|
return "n/a" unless created && finished
|
|
203
|
-
|
|
204
|
-
if seconds < 1
|
|
205
|
-
"<1s"
|
|
206
|
-
elsif seconds < 60
|
|
207
|
-
"#{seconds}s"
|
|
208
|
-
elsif seconds < 3600
|
|
209
|
-
"#{seconds / 60}m #{seconds % 60}s"
|
|
210
|
-
else
|
|
211
|
-
"#{seconds / 3600}h #{(seconds % 3600) / 60}m"
|
|
212
|
-
end
|
|
131
|
+
format_duration((finished - created).to_i)
|
|
213
132
|
end
|
|
214
133
|
end
|
|
215
134
|
end
|