lib/canvas_sync/job_batches/sidekiq/web.rb



begin
  require "sidekiq/web"
rescue LoadError
  # client-only usage
end

require_relative "web/helpers"

module CanvasSync::JobBatches::Sidekiq
  module Web
    DEV_MODE = (defined?(Rails) && !Rails.env.production?) || !!ENV["SIDEKIQ_WEB_TESTING"]
    Sidekiq::WebHelpers::SAFE_QPARAMS << 'all_batches'
    Sidekiq::WebHelpers::SAFE_QPARAMS << 'count'

    def self.registered(app) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
      app.helpers do
        include Web::Helpers

        def dev_mode?
          DEV_MODE
        end
      end

      # =============== BATCHES =============== #

      app.get "/batches" do
        @count = (params['count'] || 25).to_i

        source_key = params['all_batches'] ? "batches" : "BID-ROOT-bids"
        @current_page, @total_size, @batches = page(source_key, params['page'], @count)
        @batches = @batches.map {|b, score| CanvasSync::JobBatches::Batch.new(b) }

        erb(get_template(:batches))
      end

      app.get "/batches/:bid" do
        @bid = params[:bid]
        @batch = CanvasSync::JobBatches::Batch.new(@bid)

        @tree_data = tree_data(@bid)

        @count = (params['count'] || 25).to_i
        @current_batches_page, @total_batches_size, @sub_batches = page("BID-#{@batch.bid}-bids", params['batch_page'], @count)
        @sub_batches = @sub_batches.map {|b, score| CanvasSync::JobBatches::Batch.new(b) }

        @current_jobs_page, @total_jobs_size, @jobs = page("BID-#{@batch.bid}-jids", params['job_page'], @count)
        @jobs = @jobs.map {|jid, score| jid }

        erb(get_template(:batch))
      end

      app.get "/batches/:bid/tree" do
        @bid = params[:bid]

        json(tree_data(@bid, slice: params[:slice]))
      end

      app.helpers do
        def tree_data(root_bid, slice: nil)
          tree_bids = CanvasSync::JobBatches::Batch.bid_hierarchy(root_bid, slice: slice)

          CanvasSync::JobBatches::Batch.redis do |r|
            layer_data = ->(layer, parent = nil) {
              bid = layer[0]
              batch = CanvasSync::JobBatches::Batch.new(bid)

              jobs_total = r.hget("BID-#{bid}", "job_count").to_i
              jobs_pending = r.hget("BID-#{bid}", 'pending').to_i
              jobs_failed = r.scard("BID-#{bid}-failed").to_i
              jobs_success = jobs_total - jobs_pending

              batches_total = r.hget("BID-#{bid}", 'children').to_i
              batches_success = r.scard("BID-#{bid}-batches-success").to_i
              batches_pending = batches_total - batches_success
              batches_failed = r.scard("BID-#{bid}-batches-failed").to_i

              status = 'in_progress'
              status = 'complete' if batches_pending == batches_failed && jobs_pending == jobs_failed
              status = 'success' if batches_pending == 0 && jobs_pending == 0
              status = 'deleted' if bid != root_bid && !batch.parent_bid

              {
                bid: bid,
                created_at: r.hget("BID-#{bid}", 'created_at'),
                status: status,
                parent_bid: parent ? parent.bid : batch.parent_bid,
                description: batch.description,
                jobs: {
                  pending_count: jobs_pending,
                  successful_count: jobs_success,
                  failed_count: jobs_failed,
                  total_count: jobs_total,
                  # items: batches.map{|b| layer_data[b] },
                },
                batches: {
                  pending_count: batches_pending,
                  successful_count: batches_success,
                  failed_count: batches_failed,
                  total_count: batches_total,
                  items: layer[1].map{|b| layer_data[b, batch] },
                },
              }
            }

            data = layer_data[tree_bids]
            data[:batches][:slice] = slice if slice
            data
          end
        end

        def format_context(batch)
          bits = []
          own_keys = batch.context.own.keys
          batch.context.flatten.each do |k,v|
            added = own_keys.include? k
            bits << "  <span class=\"key #{added ? 'own' : 'inherited'}\">\"#{k}\": #{v.to_json},</span>"
          end
          bits = [
            "{ // <span class=\"own\">Added</span> / <span class=\"inherited\">Inherited</span>",
            *bits,
            '}'
          ]
          bits.join("\n")
        end
      end

      app.post "/batches/all" do
        if params['delete']
          drain_zset('batches') do |batches|
            batches.each do |bid|
              CanvasSync::JobBatches::Batch.cleanup_redis(bid)
            end
          end
        end

        redirect "#{root_path}batches"
      end

      app.post "/batches/:bid" do
        @bid = params[:bid]
        @batch = CanvasSync::JobBatches::Batch.new(@bid)

        if params['delete']
          CanvasSync::JobBatches::Batch.delete_prematurely!(@bid)
        end

        redirect_with_query("#{root_path}batches")
      end

      # =============== POOLS =============== #

      app.get "/pools" do
        @count = (params['count'] || 25).to_i
        @current_page, @total_size, @pools = page('pools', params['page'], @count)
        @pools = @pools.map {|b, score| CanvasSync::JobBatches::Pool.new(b) }

        erb(get_template(:pools))
      end

      app.get "/pools/:pid" do
        @pid = params[:pid]
        @pool = CanvasSync::JobBatches::Pool.new(@pid)

        @count = (params['count'] || 25).to_i
        @current_jobs_page, @total_jobs_size, @jobs = page("POOLID-#{@pool.pid}-jobs", params['job_page'], @count)
        @jobs = @jobs.map {|desc, score=nil| JSON.parse(desc)[0] }

        erb(get_template(:pool))
      end

      app.post "/pools/all" do
        if params['delete']
          drain_zset('pools') do |pools|
            pools.each do |pid|
              CanvasSync::JobBatches::Pool.from_pid(pid).cleanup_redis
            end
          end
        end

        redirect "#{root_path}pools"
      end

      app.post "/pools/:pid" do
        @pid = params[:pid]
        @pool = CanvasSync::JobBatches::Pool.from_pid(@pid)

        if params['delete']
          @pool.cleanup_redis
        end

        redirect_with_query("#{root_path}pools")
      end
    end
  end
end

if defined?(::Sidekiq::Web)
  rules = []
  rules = [[:all, {"Cache-Control" => "public, max-age=86400"}]] unless CanvasSync::JobBatches::Sidekiq::Web::DEV_MODE

  ::Sidekiq::Web.use Rack::Static, urls: ["/batches_assets"],
                                   root: File.expand_path("#{File.dirname(__FILE__)}/web"),
                                   cascade: true,
                                   header_rules: rules

  ::Sidekiq::Web.register CanvasSync::JobBatches::Sidekiq::Web
  ::Sidekiq::Web.tabs["Batches"] = "batches"
  ::Sidekiq::Web.tabs["Pools"] = "pools"
  ::Sidekiq::Web.settings.locales << File.join(File.dirname(__FILE__), "locales")
end