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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7da49b3d427ff6aad77da49fbf6aedcbaf9ba70b
4
- data.tar.gz: 9ac4dcc0972bba86b7d251ef73be6297fc19627b
3
+ metadata.gz: dbc7d7e3dd3c20f59925c1a0cbe8e31ece3226ae
4
+ data.tar.gz: f3bcbf4a9450956e2c5904d29f86325d908400a9
5
5
  SHA512:
6
- metadata.gz: 61442fd3a766b589dcaed9570320e7aebddbc7b695ad1ef7e65dba6b0f976c0441b752dd19104e76aa99c646445dc23b0482954a5ac0ee692e438bfb185cb9ce
7
- data.tar.gz: 6f7b1463ba5e4d27fcff502a7136b7ddec1c8b82e4605fd8767f9bea681fe3b9ab4069f6224c313a5c93ad963f0b1af06ef063a315af39481ff562365cef2a28
6
+ metadata.gz: 322b1b7e58f8d42bbb5d67d272358e54593d39f7427403d897554eeb3b567e9c3e5160f8e8e48617067557b03912afc6f34f67a62ea7c45246eedee0125688ff
7
+ data.tar.gz: 6131b8bc59458b6ed620dc970bd074e5ba28494a53b7e17e8499c1fe1eb11c003d52fdfe3a11908a0347acf380c19984e11ea5d0d2d678d7e7156c3a53612f15
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Sidekiq
2
2
  module Hierarchy
3
- VERSION = '2.0.0'
3
+ VERSION = '2.0.1'
4
4
  end
5
5
  end
@@ -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
- <%= job.status %> <%= safe_relative_time(status_updated_at(job)) %>
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 :_workflow_tree_node, locals: {job: child} %>
18
+ <%= erb subtree_template(child), locals: {job: child} %>
17
19
  <% end %>
18
20
  </ul>
19
21
  <% end %>
@@ -9,4 +9,4 @@
9
9
  <%= erb :_job_timings, locals: {job: @job} %>
10
10
 
11
11
  <h4>Job Tree</h4>
12
- <%= erb :_workflow_tree, locals: {root: @job} %>
12
+ <%= erb :_job_tree, locals: {root: @job} %>
@@ -31,7 +31,7 @@
31
31
  <%= erb :_workflow_timings, locals: {workflow: @workflow} %>
32
32
 
33
33
  <h4>Job Tree</h4>
34
- <%= erb :_workflow_tree, locals: {root: @workflow.root} %>
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.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-04 00:00:00.000000000 Z
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>