@@ -1,10 +1,15 @@ |
||
| 1 | 1 |
$ -> |
| 2 |
- firstEventCount = null |
|
| 2 |
+ sinceId = null |
|
| 3 | 3 |
previousJobs = null |
| 4 | 4 |
|
| 5 | 5 |
if $(".job-indicator").length
|
| 6 | 6 |
check = -> |
| 7 |
- $.getJSON "/worker_status", (json) -> |
|
| 7 |
+ query = |
|
| 8 |
+ if sinceId? |
|
| 9 |
+ '?since_id=' + sinceId |
|
| 10 |
+ else |
|
| 11 |
+ '' |
|
| 12 |
+ $.getJSON "/worker_status" + query, (json) -> |
|
| 8 | 13 |
for method in ['pending', 'awaiting_retry', 'recent_failures'] |
| 9 | 14 |
count = json[method] |
| 10 | 15 |
elem = $(".job-indicator[role=#{method}]")
|
@@ -23,16 +28,17 @@ $ -> |
||
| 23 | 28 |
if elem.is(":visible")
|
| 24 | 29 |
elem.tooltip('destroy').fadeOut()
|
| 25 | 30 |
|
| 26 |
- firstEventCount = json.event_count unless firstEventCount? |
|
| 27 |
- if firstEventCount? && json.event_count > firstEventCount |
|
| 31 |
+ if sinceId? && json.event_count > 0 |
|
| 28 | 32 |
$("#event-indicator").tooltip('destroy').
|
| 29 |
- tooltip(title: "Click to reload", delay: 0, placement: "bottom", trigger: "hover"). |
|
| 33 |
+ tooltip(title: "Click to see the events", delay: 0, placement: "bottom", trigger: "hover"). |
|
| 34 |
+ find('a').attr(href: json.events_url).end().
|
|
| 30 | 35 |
fadeIn(). |
| 31 | 36 |
find(".number").
|
| 32 |
- text(json.event_count - firstEventCount) |
|
| 37 |
+ text(json.event_count) |
|
| 33 | 38 |
else |
| 34 | 39 |
$("#event-indicator").tooltip('destroy').fadeOut()
|
| 35 | 40 |
|
| 41 |
+ sinceId ?= json.max_id |
|
| 36 | 42 |
currentJobs = [json.pending, json.awaiting_retry, json.recent_failures] |
| 37 | 43 |
if document.location.pathname == '/jobs' && $(".modal[aria-hidden=false]").length == 0 && previousJobs? && previousJobs.join(',') != currentJobs.join(',')
|
| 38 | 44 |
$.get '/jobs', (data) => |
@@ -42,7 +48,3 @@ $ -> |
||
| 42 | 48 |
window.workerCheckTimeout = setTimeout check, 2000 |
| 43 | 49 |
|
| 44 | 50 |
check() |
| 45 |
- |
|
| 46 |
- $("#event-indicator a").on "click", (e) ->
|
|
| 47 |
- e.preventDefault() |
|
| 48 |
- window.location.reload() |
@@ -20,6 +20,20 @@ |
||
| 20 | 20 |
} |
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 |
+.table-striped > tbody > tr.hl {
|
|
| 24 |
+ &:nth-child(odd) {
|
|
| 25 |
+ > td, > th {
|
|
| 26 |
+ background-color: #ffeecc; |
|
| 27 |
+ } |
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ &:nth-child(even) {
|
|
| 31 |
+ > td, > th {
|
|
| 32 |
+ background-color: #f9e8c6; |
|
| 33 |
+ } |
|
| 34 |
+ } |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 23 | 37 |
table.events {
|
| 24 | 38 |
.payload {
|
| 25 | 39 |
color: #999; |
@@ -1,12 +1,32 @@ |
||
| 1 | 1 |
class WorkerStatusController < ApplicationController |
| 2 | 2 |
def show |
| 3 |
- start = Time.now.to_f |
|
| 4 |
- render :json => {
|
|
| 5 |
- :pending => Delayed::Job.where("run_at <= ? AND locked_at IS NULL AND attempts = 0", Time.now).count,
|
|
| 6 |
- :awaiting_retry => Delayed::Job.where("failed_at IS NULL AND attempts > 0").count,
|
|
| 7 |
- :recent_failures => Delayed::Job.where("failed_at IS NOT NULL AND failed_at > ?", 5.days.ago).count,
|
|
| 8 |
- :event_count => current_user.events.count, |
|
| 9 |
- :compute_time => Time.now.to_f - start |
|
| 3 |
+ start = Time.now |
|
| 4 |
+ events = current_user.events |
|
| 5 |
+ |
|
| 6 |
+ if params[:since_id].present? |
|
| 7 |
+ since_id = params[:since_id].to_i |
|
| 8 |
+ events = events.where('id > ?', since_id)
|
|
| 9 |
+ end |
|
| 10 |
+ |
|
| 11 |
+ result = events.select('COUNT(id) AS count', 'MIN(id) AS min_id', 'MAX(id) AS max_id').first
|
|
| 12 |
+ count, min_id, max_id = result.count, result.min_id, result.max_id |
|
| 13 |
+ |
|
| 14 |
+ case max_id |
|
| 15 |
+ when nil |
|
| 16 |
+ when min_id |
|
| 17 |
+ events_url = events_path(hl: max_id) |
|
| 18 |
+ else |
|
| 19 |
+ events_url = events_path(hl: "#{min_id}-#{max_id}")
|
|
| 20 |
+ end |
|
| 21 |
+ |
|
| 22 |
+ render json: {
|
|
| 23 |
+ pending: Delayed::Job.pending.where("run_at <= ?", start).count,
|
|
| 24 |
+ awaiting_retry: Delayed::Job.awaiting_retry.count, |
|
| 25 |
+ recent_failures: Delayed::Job.failed.where('failed_at > ?', 5.days.ago).count,
|
|
| 26 |
+ event_count: count, |
|
| 27 |
+ max_id: max_id || 0, |
|
| 28 |
+ events_url: events_url, |
|
| 29 |
+ compute_time: Time.now - start |
|
| 10 | 30 |
} |
| 11 | 31 |
end |
| 12 | 32 |
end |
@@ -71,4 +71,25 @@ module ApplicationHelper |
||
| 71 | 71 |
service_label_text(service) |
| 72 | 72 |
].join.html_safe, class: "label label-default label-service service-#{service.provider}"
|
| 73 | 73 |
end |
| 74 |
+ |
|
| 75 |
+ def highlighted?(id) |
|
| 76 |
+ @highlighted_ranges ||= |
|
| 77 |
+ case value = params[:hl].presence |
|
| 78 |
+ when String |
|
| 79 |
+ value.split(/,/).flat_map { |part|
|
|
| 80 |
+ case part |
|
| 81 |
+ when /\A(\d+)\z/ |
|
| 82 |
+ (part.to_i)..(part.to_i) |
|
| 83 |
+ when /\A(\d+)?-(\d+)?\z/ |
|
| 84 |
+ ($1 ? $1.to_i : 1)..($2 ? $2.to_i : Float::INFINITY) |
|
| 85 |
+ else |
|
| 86 |
+ [] |
|
| 87 |
+ end |
|
| 88 |
+ } |
|
| 89 |
+ else |
|
| 90 |
+ [] |
|
| 91 |
+ end |
|
| 92 |
+ |
|
| 93 |
+ @highlighted_ranges.any? { |range| range.cover?(id) }
|
|
| 94 |
+ end |
|
| 74 | 95 |
end |
@@ -18,7 +18,7 @@ |
||
| 18 | 18 |
|
| 19 | 19 |
<% @events.each do |event| %> |
| 20 | 20 |
<% next unless event.agent %> |
| 21 |
- <tr> |
|
| 21 |
+ <%= content_tag :tr, class: (highlighted?(event.id) ? 'hl' : nil) do %> |
|
| 22 | 22 |
<td><%= link_to event.agent.name, agent_path(event.agent) %></td> |
| 23 | 23 |
<td title='<%= event.created_at %>'><%= time_ago_in_words event.created_at %> ago</td> |
| 24 | 24 |
<td class='payload'><%= truncate event.payload.to_json, :length => 90, :omission => "" %></td> |
@@ -29,12 +29,12 @@ |
||
| 29 | 29 |
<%= link_to 'Delete', event_path(event), method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-default" %>
|
| 30 | 30 |
</div> |
| 31 | 31 |
</td> |
| 32 |
- </tr> |
|
| 32 |
+ <% end %> |
|
| 33 | 33 |
<% end %> |
| 34 | 34 |
</table> |
| 35 | 35 |
</div> |
| 36 | 36 |
|
| 37 |
- <%= paginate @events, :theme => 'twitter-bootstrap-3' %> |
|
| 37 |
+ <%= paginate @events, params: params.slice(:hl), theme: 'twitter-bootstrap-3' %> |
|
| 38 | 38 |
|
| 39 | 39 |
<br /> |
| 40 | 40 |
|
@@ -7,3 +7,9 @@ Delayed::Worker.delay_jobs = !Rails.env.test? |
||
| 7 | 7 |
|
| 8 | 8 |
# Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'delayed_job.log'))
|
| 9 | 9 |
# Delayed::Worker.logger.level = Logger::DEBUG |
| 10 |
+ |
|
| 11 |
+class Delayed::Job |
|
| 12 |
+ scope :pending, ->{ where("locked_at IS NULL AND attempts = 0") }
|
|
| 13 |
+ scope :awaiting_retry, ->{ where("failed_at IS NULL AND attempts > 0") }
|
|
| 14 |
+ scope :failed, -> { where("failed_at IS NOT NULL") }
|
|
| 15 |
+end |
@@ -143,4 +143,36 @@ describe ApplicationHelper do |
||
| 143 | 143 |
expect(elem).to be_a Nokogiri::XML::Element |
| 144 | 144 |
end |
| 145 | 145 |
end |
| 146 |
+ |
|
| 147 |
+ describe '#highlighted?' do |
|
| 148 |
+ it 'understands hl=6-8' do |
|
| 149 |
+ stub(params).[](:hl) { '6-8' }
|
|
| 150 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [6, 7, 8]
|
|
| 151 |
+ end |
|
| 152 |
+ |
|
| 153 |
+ it 'understands hl=1,3-4,9' do |
|
| 154 |
+ stub(params).[](:hl) { '1,3-4,9' }
|
|
| 155 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [1, 3, 4, 9]
|
|
| 156 |
+ end |
|
| 157 |
+ |
|
| 158 |
+ it 'understands hl=8-' do |
|
| 159 |
+ stub(params).[](:hl) { '8-' }
|
|
| 160 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [8, 9, 10]
|
|
| 161 |
+ end |
|
| 162 |
+ |
|
| 163 |
+ it 'understands hl=-2' do |
|
| 164 |
+ stub(params).[](:hl) { '-2' }
|
|
| 165 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [1, 2]
|
|
| 166 |
+ end |
|
| 167 |
+ |
|
| 168 |
+ it 'understands hl=-' do |
|
| 169 |
+ stub(params).[](:hl) { '-' }
|
|
| 170 |
+ expect((1..10).select { |i| highlighted?(i) }).to eq [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
| 171 |
+ end |
|
| 172 |
+ |
|
| 173 |
+ it 'is OK with no hl' do |
|
| 174 |
+ stub(params).[](:hl) { nil }
|
|
| 175 |
+ expect((1..10).select { |i| highlighted?(i) }).to be_empty
|
|
| 176 |
+ end |
|
| 177 |
+ end |
|
| 146 | 178 |
end |