require 'yaml'
# Extends Resque Web Based UI.
# Structure has been borrowed from ResqueScheduler.
module ResqueCleaner
module Server
def self.erb_path(filename)
File.join(File.dirname(__FILE__), 'server', 'views', filename)
end
# Pagination helper for list page.
class Paginate
attr_accessor :page_size, :page, :jobs, :url
def initialize(jobs, url, page=1, page_size=20)
@jobs = jobs
@url = url
@page = (!page || page < 1) ? 1 : page
@page_size = 20
end
def first_index
@page_size * (@page-1)
end
def last_index
last = first_index + @page_size - 1
last > @jobs.size-1 ? @jobs.size-1 : last
end
def paginated_jobs
@jobs[first_index,@page_size]
end
def first_page?
@page <= 1
end
def last_page?
@page >= max_page
end
def page_url(page)
u = @url
u += @url.include?("?") ? "&" : "?"
if page.is_a?(Symbol)
page = @page - 1 if page==:prev
page = @page + 1 if page==:next
end
u += "p=#{page}"
end
def total_size
@jobs.size
end
def max_page
((total_size-1) / @page_size) + 1
end
end
def self.included(base)
base.class_eval do
helpers do
def time_filter(id, name, value)
html = ""
end
def class_filter(id, name, klasses, value)
html = ""
end
def exception_filter(id, name, exceptions, value)
html = ""
end
def show_job_args(args)
Array(args).map { |a| a.inspect }.join("\n")
end
def text_filter(id, name, value)
html = ""
html += ""
end
end
mime_type :json, 'application/json'
get "/cleaner" do
load_library
load_cleaner_filter
@jobs = cleaner.select
@stats = { :klass => {}, :exception => {} }
@total = Hash.new(0)
@jobs.each do |job|
payload = job["payload"] || {}
klass = payload["class"] || 'UNKNOWN'
exception = job["exception"] || 'UNKNOWN'
failed_at = Time.parse job["failed_at"]
@stats[:klass][klass] ||= Hash.new(0)
@stats[:exception][exception] ||= Hash.new(0)
[
@stats[:klass][klass],
@stats[:exception][exception],
@total
].each do |stat|
stat[:total] += 1
stat[:h1] += 1 if failed_at >= hours_ago(1)
stat[:h3] += 1 if failed_at >= hours_ago(3)
stat[:d1] += 1 if failed_at >= hours_ago(24)
stat[:d3] += 1 if failed_at >= hours_ago(24*3)
stat[:d7] += 1 if failed_at >= hours_ago(24*7)
end
end
erb File.read(ResqueCleaner::Server.erb_path('cleaner.erb'))
end
get "/cleaner_list" do
load_library
load_cleaner_filter
build_urls
block = filter_block
@failed = cleaner.select(&block).reverse
@paginate = Paginate.new(@failed, @list_url, params[:p].to_i)
@klasses = cleaner.stats_by_class.keys
@exceptions = cleaner.stats_by_exception.keys
@count = cleaner.select(&block).size
erb File.read(ResqueCleaner::Server.erb_path('cleaner_list.erb'))
end
post "/cleaner_exec" do
load_library
load_cleaner_filter
build_urls
if params[:select_all_pages]!="1"
@sha1 = {}
params[:sha1].split(",").each {|s| @sha1[s] = true }
end
block = filter_block
@count =
case params[:action]
when "clear" then cleaner.clear(&block)
when "retry_and_clear" then cleaner.requeue(true,&block)
when "retry" then cleaner.requeue(false,{},&block)
end
erb File.read(ResqueCleaner::Server.erb_path('cleaner_exec.erb'))
end
get "/cleaner_dump" do
load_library
load_cleaner_filter
block = filter_block
content_type :json
JSON.pretty_generate(cleaner.select(&block))
end
post "/cleaner_stale" do
load_library
cleaner.clear_stale
redirect url_path(:cleaner)
end
end
end
def cleaner
@cleaner ||= Resque::Plugins::ResqueCleaner.new
@cleaner.print_message = false
@cleaner
end
def load_library
require 'digest/sha1'
begin
require 'yajl/json_gem' unless [].respond_to?(:to_json)
rescue Exception
require 'json'
end
end
def load_cleaner_filter
@from = params[:f]=="" ? nil : params[:f]
@to = params[:t]=="" ? nil : params[:t]
@klass = params[:c]=="" ? nil : params[:c]
@exception = params[:ex]=="" ? nil : params[:ex]
@regex = params[:regex]=="" ? nil : params[:regex]
end
def build_urls
params = {
c: @klass,
ex: @exception,
f: @from,
t: @to,
regex: @regex
}.map {|key,value| "#{key}=#{URI.encode(value.to_s)}"}.join("&")
@list_url = "cleaner_list?#{params}"
@dump_url = "cleaner_dump?#{params}"
end
def filter_block
block = lambda{|j|
(!@from || j.after?(hours_ago(@from))) &&
(!@to || j.before?(hours_ago(@to))) &&
(!@klass || j.klass?(@klass)) &&
(!@exception || j.exception?(@exception)) &&
(!@sha1 || @sha1[Digest::SHA1.hexdigest(j.to_json)]) &&
(!@regex || j.to_s =~ /#{@regex}/)
}
end
def hours_ago(h)
Time.now - h.to_i*60*60
end
Resque::Server.tabs << 'Cleaner'
end
end
Resque::Server.class_eval do
include ResqueCleaner::Server
end