sh19910711/git-contest

View on GitHub
lib/contest/driver/kattis_driver.rb

Summary

Maintainability
C
7 hrs
Test Coverage
#
# kattis_driver.rb
#
# Copyright (c) 2013-2014 Hiroyuki Sano <sh19910711 at gmail.com>
# Copyright (c) 2014 Oskar Sundström <oskar.sundstrom at gmail.com>
# Licensed under the MIT-License.
#
# An official Python submission script can be found at
# https://open.kattis.com/help/submit
#
# Using the Python script you would write:
#  $ python3 submit3.py -p aaah Main.java
#
# To do the same thing using git-contest you instead write:
#  $ git contest submit kattis -p aaah -s Main.java
#

require 'contest/driver/common'

module Contest
  module Driver
    class KattisDriver < DriverBase
      def initialize_ext
        @client = Mechanize.new {|agent|
          agent.user_agent_alias = 'Windows IE 7'
        }
      end

      def get_opts_ext
        define_options do
          opt(
            :contest_id,
            "Contest ID (Ex: open, kth, liu, etc...)",
            :type => :string,
            :required => false,
          )
          opt(
            :problem_id,
            "Problem ID (Ex: aaah, listgame2, etc...)",
            :type => :string,
            :required => true,
          )
        end
      end

      def get_site_name
        "Kattis"
      end

      def get_desc
        "Kattis (URL: https://open.kattis.com/)"
      end

      def get_problem_id(options)
        "#{options[:problem_id]}"
      end

      def resolve_language(label)
        case label
        when 'cpp'
          return '1'
        when 'c'
          return '2'
        when 'java'
          return '3'
        when 'python2'
          return '6'
        when 'python3'
          return '8'
        when 'cs'
          return '9'
        when 'golang'
          return '10'
        when 'objc'
           return '11'
        when 'haskell'
            return '12'
        when 'prolog'
            return '13'
        when 'javascript'
            return '14'
        when 'php'
            return '15'
        else
          abort 'unknown language'
        end
      end

      def submit_ext
        trigger 'start'
        problem_id = @options[:problem_id]

        if (@options[:contest_id])
          subdomain = @options[:contest_id]
        else
          subdomain = 'open'
        end

        # Login
        trigger 'before_login'
        login_page = @client.get "https://#{subdomain}.kattis.com/login?email_login=true"
        login_page.form_with(:action => '/login/email') do |form|
          form.user = @config["user"]
          form.password = @config["password"]
        end.submit
        trigger 'after_login'

        # Submit
        trigger 'before_submit', @options
        submit_page = @client.get "https://#{subdomain}.kattis.com/submit"
        res_page = submit_page.form_with(:name => 'upload') do |form|
          form.problem = problem_id
          form['lang'] = @options[:language]
          form.sub_code = File.read(@options[:source])
          # Use file name as main class for Java
          if (@options[:language] == resolve_language('java'))
            form['mainclass'] = @options[:source].rpartition('.')[0]
          end
          form.submit(form.button_with(:name => 'submit'))
        end.submit
        trigger 'after_submit'

        # Result
        trigger 'before_wait'
        user = @config['user']
        doc = Nokogiri::HTML(res_page.body)
        # Check for error messages
        error = doc.xpath('//p[@class="error"]')[0];
        if ((/Problem ID not found in database./ =~ error) ||
            (/Problem-id inte funnet i databasen./ =~ error))
          abort "Problem ID not found in database."
        end
        sleep 2
        submissions_page = @client.get "https://#{subdomain}.kattis.com/users/#{user}"
        submission_id = get_submission_id(submissions_page.body)
        status = get_status_wait(submission_id, subdomain)
        trigger(
          'after_wait',
          {
            :submission_id => submission_id,
            :status => status,
            :result => get_commit_message(status),
          }
        )

        trigger 'finish'
        get_commit_message(status)
      end

      def is_wait_status(status)
        case status
        when "Running", "Compiling", ""
          true
        else
          false
        end
      end

      def get_status_wait(submission_id, subdomain)
        submission_id = submission_id.to_s
        # Wait for result
        12.times do
          sleep 10
          submission_page = @client.get "https://#{subdomain}.kattis.com/submissions/#{submission_id}"
          status = get_submission_status(submission_id, submission_page.body)
          return status unless is_wait_status status
          trigger 'retry'
        end
        trigger 'timeout'
        return 'timeout'
      end

      def get_submission_id(body)
        doc = Nokogiri::HTML(body)
        return doc.xpath('//*[@id="wrapper"]/div/div[2]/section/table/tbody/tr[1]/td[1]/a')[0].inner_text().strip
      end

      def get_submission_status(submission_id, body)
        doc = Nokogiri::HTML(body)
        return doc.xpath('//*[@id="judge_table"]/tbody/tr[1]/td[4]/span').inner_text().strip
      end
    end
  end
end