sidekiq-hierarchy 2.0.0 → 2.0.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/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>
|