sidekiq-hierarchy 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/sidekiq/hierarchy/version.rb +1 -1
- data/lib/sidekiq/hierarchy/web.rb +12 -53
- data/lib/sidekiq/hierarchy/web/helpers.rb +99 -0
- data/web/views/_job_tree.erb +98 -0
- data/web/views/_job_tree_collapsed.erb +13 -0
- data/web/views/{_workflow_tree_node.erb → _job_tree_node.erb} +5 -3
- data/web/views/job.erb +1 -1
- data/web/views/workflow.erb +1 -1
- metadata +6 -4
- data/web/views/_workflow_tree.erb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dbc7d7e3dd3c20f59925c1a0cbe8e31ece3226ae
|
4
|
+
data.tar.gz: f3bcbf4a9450956e2c5904d29f86325d908400a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 322b1b7e58f8d42bbb5d67d272358e54593d39f7427403d897554eeb3b567e9c3e5160f8e8e48617067557b03912afc6f34f67a62ea7c45246eedee0125688ff
|
7
|
+
data.tar.gz: 6131b8bc59458b6ed620dc970bd074e5ba28494a53b7e17e8499c1fe1eb11c003d52fdfe3a11908a0347acf380c19984e11ea5d0d2d678d7e7156c3a53612f15
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
4
|
|
5
|
+
## [2.0.1] - 2015-12-07
|
6
|
+
### Changed
|
7
|
+
- Collapse job tree in web UI in case of huge workflows, keeping things snappy
|
8
|
+
- Allow lazy loading of workflow subtrees in web UI for easy navigation of large trees
|
9
|
+
- Display jobs with their runtime instead of their time of completion to ease spotting of bottlenecks
|
10
|
+
|
5
11
|
## [2.0.0] - 2015-12-04
|
6
12
|
### Changed
|
7
13
|
- HTTP headers renamed to make roles more clear
|
@@ -3,62 +3,12 @@
|
|
3
3
|
# not to leave this open in a tab forever.
|
4
4
|
# Sidekiq seems to use Bootstrap 3.0.0 currently; find docs at
|
5
5
|
# http://bootstrapdocs.com/v3.0.0/docs/
|
6
|
+
|
7
|
+
require 'sidekiq/hierarchy/web/helpers'
|
8
|
+
|
6
9
|
module Sidekiq
|
7
10
|
module Hierarchy
|
8
11
|
module Web
|
9
|
-
module Helpers
|
10
|
-
# Override find_template logic to process arrays of view directories
|
11
|
-
# warning: this may be incompatible with other overrides of find_template,
|
12
|
-
# though that really shouldn't happen if they match the method contract
|
13
|
-
def find_template(views, name, engine, &block)
|
14
|
-
Array(views).each do |view_dir|
|
15
|
-
super(view_dir, name, engine, &block)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def job_url(job=nil)
|
20
|
-
"#{root_path}hierarchy/jobs/#{job.jid if job}"
|
21
|
-
end
|
22
|
-
|
23
|
-
def workflow_url(workflow=nil)
|
24
|
-
"#{root_path}hierarchy/workflows/#{workflow.jid if workflow}"
|
25
|
-
end
|
26
|
-
|
27
|
-
def workflow_set_url(status)
|
28
|
-
"#{root_path}hierarchy/workflow_sets/#{status}"
|
29
|
-
end
|
30
|
-
|
31
|
-
def safe_relative_time(timestamp)
|
32
|
-
timestamp.nil? ? '-' : relative_time(timestamp)
|
33
|
-
end
|
34
|
-
|
35
|
-
def status_updated_at(job)
|
36
|
-
case job.status
|
37
|
-
when :enqueued
|
38
|
-
job.enqueued_at
|
39
|
-
when :running, :requeued
|
40
|
-
job.run_at
|
41
|
-
when :complete
|
42
|
-
job.complete_at
|
43
|
-
when :failed
|
44
|
-
job.failed_at
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def bootstrap_status(status)
|
49
|
-
case status
|
50
|
-
when :enqueued, :requeued
|
51
|
-
'warning'
|
52
|
-
when :running
|
53
|
-
'info'
|
54
|
-
when :complete
|
55
|
-
'success'
|
56
|
-
when :failed
|
57
|
-
'danger'
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
12
|
VIEW_PATH = File.expand_path('../../../../web/views', __FILE__)
|
63
13
|
PER_PAGE = 20
|
64
14
|
|
@@ -149,6 +99,15 @@ module Sidekiq
|
|
149
99
|
halt 404
|
150
100
|
end
|
151
101
|
end
|
102
|
+
|
103
|
+
app.get %r{\A/hierarchy/jobs/(\h{24})/subtree\z} do |jid|
|
104
|
+
job = Job.find(jid)
|
105
|
+
if job.exists?
|
106
|
+
erb :_job_tree_node, {layout: false}, {job: job}
|
107
|
+
else
|
108
|
+
halt 404
|
109
|
+
end
|
110
|
+
end
|
152
111
|
end
|
153
112
|
end
|
154
113
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
module Hierarchy
|
3
|
+
module Web
|
4
|
+
module Helpers
|
5
|
+
TIME_TO_WORD = {
|
6
|
+
29030400 => 'year',
|
7
|
+
2419200 => 'month',
|
8
|
+
604800 => 'week',
|
9
|
+
86400 => 'day',
|
10
|
+
3600 => 'hour',
|
11
|
+
60 => 'minute',
|
12
|
+
1 => 'second',
|
13
|
+
}
|
14
|
+
|
15
|
+
COLLAPSED_SUBTREE_THRESHOLD = 500
|
16
|
+
|
17
|
+
|
18
|
+
### TEMPLATE HELPERS
|
19
|
+
|
20
|
+
# Override find_template logic to process arrays of view directories
|
21
|
+
# warning: this may be incompatible with other overrides of find_template,
|
22
|
+
# though that really shouldn't happen if they match the method contract
|
23
|
+
def find_template(views, name, engine, &block)
|
24
|
+
Array(views).each do |view_dir|
|
25
|
+
super(view_dir, name, engine, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def subtree_template(job)
|
30
|
+
if job.subtree_size > COLLAPSED_SUBTREE_THRESHOLD
|
31
|
+
:_job_tree_collapsed
|
32
|
+
else
|
33
|
+
:_job_tree_node
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
### ROUTE HELPERS
|
39
|
+
|
40
|
+
def job_url(job=nil)
|
41
|
+
"#{root_path}hierarchy/jobs/#{job.jid if job}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def workflow_url(workflow=nil)
|
45
|
+
"#{root_path}hierarchy/workflows/#{workflow.jid if workflow}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def workflow_set_url(status)
|
49
|
+
"#{root_path}hierarchy/workflow_sets/#{status}"
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
### FORMATTING HELPERS
|
54
|
+
|
55
|
+
def safe_relative_time(timestamp)
|
56
|
+
timestamp.nil? ? '-' : relative_time(timestamp)
|
57
|
+
end
|
58
|
+
|
59
|
+
def time_in_words(time)
|
60
|
+
divisor, period = TIME_TO_WORD.select { |secs, _| time.ceil >= secs }.max_by(&:first)
|
61
|
+
duration = (time / divisor).to_i
|
62
|
+
|
63
|
+
"#{duration} #{period}#{'s' unless duration == 1}"
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
### HUMANIZATION HELPERS
|
68
|
+
|
69
|
+
def bootstrap_status(status)
|
70
|
+
case status
|
71
|
+
when :enqueued, :requeued
|
72
|
+
'warning'
|
73
|
+
when :running
|
74
|
+
'info'
|
75
|
+
when :complete
|
76
|
+
'success'
|
77
|
+
when :failed
|
78
|
+
'danger'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def status_in_words(job)
|
83
|
+
case job.status
|
84
|
+
when :enqueued
|
85
|
+
"enqueued #{safe_relative_time(job.enqueued_at)}"
|
86
|
+
when :requeued
|
87
|
+
"requeued #{safe_relative_time(job.enqueued_at)}"
|
88
|
+
when :running
|
89
|
+
"running #{time_in_words(Time.now - job.run_at)}"
|
90
|
+
when :complete
|
91
|
+
"complete in #{time_in_words(job.complete_at - job.run_at)}"
|
92
|
+
when :failed
|
93
|
+
"failed in #{time_in_words(job.failed_at - job.run_at)}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
<style>
|
2
|
+
/* Adapted from http://www.cssscript.com/demo/minimalist-tree-view-in-pure-css */
|
3
|
+
|
4
|
+
ul.job_tree {
|
5
|
+
padding-left: 0px;
|
6
|
+
}
|
7
|
+
|
8
|
+
.job_tree li {
|
9
|
+
list-style-type: none;
|
10
|
+
margin: 10px;
|
11
|
+
position: relative;
|
12
|
+
}
|
13
|
+
|
14
|
+
.job_tree li::before {
|
15
|
+
content: "";
|
16
|
+
position: absolute;
|
17
|
+
top: -7px;
|
18
|
+
left: -20px;
|
19
|
+
border-left: 1px solid #ccc;
|
20
|
+
border-bottom: 1px solid #ccc;
|
21
|
+
border-radius: 0 0 0 0px;
|
22
|
+
width: 20px;
|
23
|
+
height: 15px;
|
24
|
+
}
|
25
|
+
|
26
|
+
.job_tree li::after {
|
27
|
+
position: absolute;
|
28
|
+
content: "";
|
29
|
+
top: 8px;
|
30
|
+
left: -20px;
|
31
|
+
border-left: 1px solid #ccc;
|
32
|
+
border-top: 1px solid #ccc;
|
33
|
+
border-radius: 0px 0 0 0;
|
34
|
+
width: 20px;
|
35
|
+
height: 100%;
|
36
|
+
}
|
37
|
+
|
38
|
+
.job_tree li:last-child::after {
|
39
|
+
display: none;
|
40
|
+
}
|
41
|
+
|
42
|
+
.job_tree li:last-child:before{
|
43
|
+
border-radius: 0 0 0 5px;
|
44
|
+
}
|
45
|
+
|
46
|
+
ul.job_tree > li:first-child::before {
|
47
|
+
display: none;
|
48
|
+
}
|
49
|
+
|
50
|
+
ul.job_tree > li:first-child::after {
|
51
|
+
border-radius: 5px 0 0 0;
|
52
|
+
}
|
53
|
+
|
54
|
+
.job_tree li a {
|
55
|
+
border-radius: 5px;
|
56
|
+
padding: 2px 5px;
|
57
|
+
}
|
58
|
+
|
59
|
+
.job_tree li a:hover, .job_tree li a:focus {
|
60
|
+
background: #ccc;
|
61
|
+
color: #000;
|
62
|
+
text-decoration: none;
|
63
|
+
}
|
64
|
+
|
65
|
+
.job_tree li a:hover+ul li a, .job_tree li a:focus+ul li a {
|
66
|
+
color: #000;
|
67
|
+
}
|
68
|
+
|
69
|
+
.job_tree li a:hover+ul li::after, .job_tree li a:focus+ul li::after,
|
70
|
+
.job_tree li a:hover+ul li::before, .job_tree li a:focus+ul li::before
|
71
|
+
.job_tree li a:hover+ul::before, .job_tree li a:focus+ul::before
|
72
|
+
.job_tree li a:hover+ul ul::before, .job_tree li a:focus+ul ul::before{
|
73
|
+
border-color: #000;
|
74
|
+
}
|
75
|
+
</style>
|
76
|
+
|
77
|
+
<script>
|
78
|
+
$(function() {
|
79
|
+
// hook collapsed job nodes to permit lazy in-place expansion
|
80
|
+
function attachListeners() {
|
81
|
+
$(".collapsed_job_node a").click(function(e) {
|
82
|
+
e.preventDefault();
|
83
|
+
// replace collapsed node with tree from AJAX
|
84
|
+
$.get(e.target.href + "/subtree", function(subtree) {
|
85
|
+
$(e.target.parentNode).replaceWith(subtree);
|
86
|
+
attachListeners() // attach listeners to newly inserted nodes
|
87
|
+
})
|
88
|
+
})
|
89
|
+
}
|
90
|
+
attachListeners() // perform initial attachment
|
91
|
+
})
|
92
|
+
</script>
|
93
|
+
|
94
|
+
<ul class="job_tree">
|
95
|
+
<% unless root.root? %><ul><% end %>
|
96
|
+
<%= erb subtree_template(root), locals: {job: root} %>
|
97
|
+
<% unless root.root? %></ul><% end %>
|
98
|
+
</ul>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<li class="collapsed_job_node">
|
2
|
+
<a href="<%= root_path %>hierarchy/jobs/<%= job.jid %>">
|
3
|
+
<span title="<%= job.jid %>">
|
4
|
+
<%= job.info['class'] %>
|
5
|
+
</span>
|
6
|
+
|
7
|
+
(<%= job.info['queue'] %>)
|
8
|
+
|
9
|
+
<%- descendants = job.subtree_size - 1 %>
|
10
|
+
<%- finished_descendants = job.finished_subtree_size - 1 %>
|
11
|
+
[+ <%= descendants %> more, <%= descendants - finished_descendants %> running]
|
12
|
+
</a>
|
13
|
+
</li>
|
@@ -1,11 +1,13 @@
|
|
1
|
-
<li>
|
1
|
+
<li class="job_node">
|
2
2
|
<a href="<%= root_path %>hierarchy/jobs/<%= job.jid %>">
|
3
3
|
<span title="<%= job.jid %>">
|
4
4
|
<%= job.info['class'] %>
|
5
5
|
</span>
|
6
|
+
|
6
7
|
(<%= job.info['queue'] %>)
|
8
|
+
|
7
9
|
<span class="label label-<%= bootstrap_status(job.status) %>">
|
8
|
-
<%=
|
10
|
+
<%= status_in_words(job) %>
|
9
11
|
</span>
|
10
12
|
</a>
|
11
13
|
|
@@ -13,7 +15,7 @@
|
|
13
15
|
<% if children.any? %>
|
14
16
|
<ul>
|
15
17
|
<% children.each do |child| %>
|
16
|
-
<%= erb
|
18
|
+
<%= erb subtree_template(child), locals: {job: child} %>
|
17
19
|
<% end %>
|
18
20
|
</ul>
|
19
21
|
<% end %>
|
data/web/views/job.erb
CHANGED
data/web/views/workflow.erb
CHANGED
@@ -31,7 +31,7 @@
|
|
31
31
|
<%= erb :_workflow_timings, locals: {workflow: @workflow} %>
|
32
32
|
|
33
33
|
<h4>Job Tree</h4>
|
34
|
-
<%= erb :
|
34
|
+
<%= erb :_job_tree, locals: {root: @workflow.root} %>
|
35
35
|
|
36
36
|
<h4>Jobs</h4>
|
37
37
|
<%= erb :_job_table, locals: {jobs: @workflow.jobs} %>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-hierarchy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anuj Das
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -192,12 +192,16 @@ files:
|
|
192
192
|
- lib/sidekiq/hierarchy/server/middleware.rb
|
193
193
|
- lib/sidekiq/hierarchy/version.rb
|
194
194
|
- lib/sidekiq/hierarchy/web.rb
|
195
|
+
- lib/sidekiq/hierarchy/web/helpers.rb
|
195
196
|
- lib/sidekiq/hierarchy/workflow.rb
|
196
197
|
- lib/sidekiq/hierarchy/workflow_set.rb
|
197
198
|
- sidekiq-hierarchy.gemspec
|
198
199
|
- web/views/_job_progress_bar.erb
|
199
200
|
- web/views/_job_table.erb
|
200
201
|
- web/views/_job_timings.erb
|
202
|
+
- web/views/_job_tree.erb
|
203
|
+
- web/views/_job_tree_collapsed.erb
|
204
|
+
- web/views/_job_tree_node.erb
|
201
205
|
- web/views/_progress_bar.erb
|
202
206
|
- web/views/_search_bar.erb
|
203
207
|
- web/views/_summary_bar.erb
|
@@ -205,8 +209,6 @@ files:
|
|
205
209
|
- web/views/_workflow_set_clear.erb
|
206
210
|
- web/views/_workflow_table.erb
|
207
211
|
- web/views/_workflow_timings.erb
|
208
|
-
- web/views/_workflow_tree.erb
|
209
|
-
- web/views/_workflow_tree_node.erb
|
210
212
|
- web/views/job.erb
|
211
213
|
- web/views/not_found.erb
|
212
214
|
- web/views/status.erb
|
@@ -1,82 +0,0 @@
|
|
1
|
-
<style>
|
2
|
-
/* Lifted from http://www.cssscript.com/demo/minimalist-tree-view-in-pure-css */
|
3
|
-
/* Alternative styling: http://www.goocode.net/css/153-pure-css-to-create-family-tree.html */
|
4
|
-
|
5
|
-
ul.workflow_tree {
|
6
|
-
padding-left: 0px;
|
7
|
-
}
|
8
|
-
|
9
|
-
.workflow_tree li {
|
10
|
-
list-style-type: none;
|
11
|
-
margin: 10px;
|
12
|
-
position: relative;
|
13
|
-
}
|
14
|
-
|
15
|
-
.workflow_tree li::before {
|
16
|
-
content: "";
|
17
|
-
position: absolute;
|
18
|
-
top: -7px;
|
19
|
-
left: -20px;
|
20
|
-
border-left: 1px solid #ccc;
|
21
|
-
border-bottom: 1px solid #ccc;
|
22
|
-
border-radius: 0 0 0 0px;
|
23
|
-
width: 20px;
|
24
|
-
height: 15px;
|
25
|
-
}
|
26
|
-
|
27
|
-
.workflow_tree li::after {
|
28
|
-
position: absolute;
|
29
|
-
content: "";
|
30
|
-
top: 8px;
|
31
|
-
left: -20px;
|
32
|
-
border-left: 1px solid #ccc;
|
33
|
-
border-top: 1px solid #ccc;
|
34
|
-
border-radius: 0px 0 0 0;
|
35
|
-
width: 20px;
|
36
|
-
height: 100%;
|
37
|
-
}
|
38
|
-
|
39
|
-
.workflow_tree li:last-child::after {
|
40
|
-
display: none;
|
41
|
-
}
|
42
|
-
|
43
|
-
.workflow_tree li:last-child:before{
|
44
|
-
border-radius: 0 0 0 5px;
|
45
|
-
}
|
46
|
-
|
47
|
-
ul.workflow_tree > li:first-child::before {
|
48
|
-
display: none;
|
49
|
-
}
|
50
|
-
|
51
|
-
ul.workflow_tree > li:first-child::after {
|
52
|
-
border-radius: 5px 0 0 0;
|
53
|
-
}
|
54
|
-
|
55
|
-
.workflow_tree li a {
|
56
|
-
border-radius: 5px;
|
57
|
-
padding: 2px 5px;
|
58
|
-
}
|
59
|
-
|
60
|
-
.workflow_tree li a:hover, .workflow_tree li a:focus {
|
61
|
-
background: #ccc;
|
62
|
-
color: #000;
|
63
|
-
text-decoration: none;
|
64
|
-
}
|
65
|
-
|
66
|
-
.workflow_tree li a:hover+ul li a, .workflow_tree li a:focus+ul li a {
|
67
|
-
color: #000;
|
68
|
-
}
|
69
|
-
|
70
|
-
.workflow_tree li a:hover+ul li::after, .workflow_tree li a:focus+ul li::after,
|
71
|
-
.workflow_tree li a:hover+ul li::before, .workflow_tree li a:focus+ul li::before
|
72
|
-
.workflow_tree li a:hover+ul::before, .workflow_tree li a:focus+ul::before
|
73
|
-
.workflow_tree li a:hover+ul ul::before, .workflow_tree li a:focus+ul ul::before{
|
74
|
-
border-color: #000;
|
75
|
-
}
|
76
|
-
</style>
|
77
|
-
|
78
|
-
<ul class="workflow_tree">
|
79
|
-
<% unless root.root? %><ul><% end %>
|
80
|
-
<%= erb :_workflow_tree_node, locals: {job: root} %>
|
81
|
-
<% unless root.root? %></ul><% end %>
|
82
|
-
</ul>
|