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