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