Exemplo n.º 1
0
def main():
    """Main function for the status grabber script.

  This script receives no positional arguments.
  """
    try:
        # Create an option parser and use it to parse the supplied arguments.
        program_version = 'GCJ status grabber {0}'.format(constants.VERSION)
        parser = optparse.OptionParser(usage='%prog [options]',
                                       version=program_version)
        parser.add_option('-l',
                          '--login',
                          action='store_true',
                          dest='renew_cookie',
                          help='Ignore the stored cookie, and log in again')
        parser.add_option(
            '-p',
            '--passwd',
            action='store',
            dest='password',
            help=('Password used to log in. You will be prompted for '
                  'a password if one is required and this flag is '
                  'left empty and there is no password in the '
                  'configuration files'))
        parser.add_option(
            '--base_dir',
            action='store',
            dest='base_dir',
            help=('Base directory used to parametrize configuration '
                  'file paths'))
        parser.set_defaults(renew_cookie=False,
                            base_dir=os.path.dirname(
                                os.path.realpath(__file__)))
        options, args = parser.parse_args()

        # Store the script location in a runtime constant, so it can be used by
        # the library to locate the configuration files.
        constants.SetRuntimeConstant('base_dir', options.base_dir)

        # Check that the number of arguments is valid.
        if len(args) != 0:
            raise error.OptionError('need no positional arguments')

        # Read user information from the config file.
        try:
            current_config = data_manager.ReadData()
            host = current_config['host']
            middleware_tokens = current_config['middleware_tokens']
            cookie = current_config['cookie']
            contest_id = current_config['contest_id']
            problems = current_config['problems']
        except KeyError as e:
            raise error.ConfigurationError(
                'Cannot find field {0} in the configuration files. Please fill the '
                'missing fields in the user configuration file.\n'.format(e))

        # Get the get middleware token used to request the user status.
        try:
            user_status_token = middleware_tokens['GetUserStatus']
        except KeyError:
            raise error.ConfigurationError(
                'Cannot find "GetUserStatus" token in configuration file. '
                'Reinitializing the contest might solve this error.\n')

        # Renew the cookie if the user requested a new login or the cookie has
        # expired.
        if google_login.CookieHasExpired(cookie):
            print 'Cookie has expired, logging into the Code Jam servers...'
            cookie = None
        if not cookie or options.renew_cookie:
            cookie = code_jam_login.Login(options.password)

        # Get user's status and check if he/she is participating.
        status = user_status.GetUserStatus(host, cookie, user_status_token,
                                           contest_id, problems)
        if status is not None:
            # Get user's submissions and use them to fix the user's status.
            submissions = user_submissions.GetUserSubmissions(
                host, cookie, contest_id, problems)
            user_status.FixStatusWithSubmissions(status, submissions)

            # Show each problem input status to the user.
            print '-' * 79
            print 'User status at contest {0}'.format(contest_id)
            print '-' * 79
            print 'Rank: {0} ({1} points)'.format(status.rank, status.points)
            for problem, problem_status in itertools.izip(
                    problems, status.problem_inputs):
                print 'Problem: {0}'.format(problem['name'])
                for input_status in problem_status:
                    print '  {0}'.format(
                        FormatProblemInputStatus(input_status))
        else:
            # Show error message, user is not participating in the contest.
            print 'User status not found at contest {0}'.format(contest_id)

    except error.OptionError as e:
        parser.print_usage()
        program_basename = os.path.basename(sys.argv[0])
        sys.stderr.write('{0}: error: {1}\n'.format(program_basename, e))
        sys.exit(1)

    except error.UserError as e:
        sys.stderr.write(str(e))
        sys.exit(1)

    except error.CommandlineError as e:
        sys.stderr.write('{0}: {1}'.format(e.__class__.__name__, e))
        sys.exit(1)
Exemplo n.º 2
0
def main():
  """Main function for the input downloader script.

  This script receives three positional arguments, the problem letter, the input
  size and the submit id.
  """
  try:
    # Create an option parser and use it to parse the supplied arguments.
    program_version = 'GCJ input downloader {0}'.format(
        constants.VERSION)
    parser = optparse.OptionParser(usage='%prog [options] problem input id',
                                   version=program_version)
    parser.add_option('-l', '--login', action='store_true', dest='renew_cookie',
                      help='Ignore the stored cookie, and log in again')
    parser.add_option('-p', '--passwd', action='store', dest='password',
                      help=('Password used to log in. You will be prompted for '
                            'a password if one is required and this flag is '
                            'left empty and there is no password in the '
                            'configuration files'))
    parser.add_option('-f', '--force', action='store_true', dest='force',
                      help=('Skip check to verify that user wants to start a '
                            'new timer'))
    parser.add_option('-d', '--data-directory', action='store',
                      dest='data_directory',
                      help='Directory with the I/O files and main source files')
    parser.add_option('-i', '--input-name', action='store', dest='input_name',
                      help='Name of the file where the input should be stored')
    parser.add_option('--base_dir', action='store', dest='base_dir',
                      help=('Base directory used to parametrize configuration '
                            'file paths'))
    parser.set_defaults(renew_cookie=False, force=False,
                        base_dir=os.path.dirname(os.path.realpath(__file__)))
    options, args = parser.parse_args()

    # Store the script location in a runtime constant, so it can be used by
    # the library to locate the configuration files.
    constants.SetRuntimeConstant('base_dir', options.base_dir)

    # Check that the number of arguments is valid.
    if len(args) != 3:
      raise error.OptionError('need 3 positional arguments')

    # Check that the problem idenfier is valid.
    problem_letter = args[0].upper()
    if len(problem_letter) != 1 or not problem_letter.isupper():
      raise error.OptionError('invalid problem {0}, must be one letter'.format(
          problem_letter))

    # Check that the submit id is a valid identifier.
    id = args[2]
    if not re.match('^\w+$', id):
      raise error.OptionError('invalid id {0}, can only have numbers, letters '
                              'and underscores'.format(id))

    # Check that the contest has been initialized.
    if not contest_manager.IsInitialized():
      raise error.ConfigurationError(
          'Contest is not initialized, please initialize the contest before '
          'trying to download input files.\n');

    # Read user and input information from the config file.
    try:
      current_config = data_manager.ReadData()
      host = current_config['host']
      user = current_config['user']
      input_spec = current_config['input_spec']
    except KeyError as e:
      raise error.ConfigurationError(
          'Cannot find all required user data in the configuration files: {0}. '
          'Please fill the missing fields in the user configuration '
          'file.\n'.format(e))

    # Check that the input type is valid.
    input_type = args[1].lower()
    if input_type not in input_spec:
      raise error.OptionError(
          'invalid input type {0}, must be one of ({1})'.format(
              input_type, ','.join(input_spec)))

    # Read current contest information from the config file.
    try:
      middleware_tokens = current_config['middleware_tokens']
      cookie = None if options.renew_cookie else current_config['cookie']
      contest_id = current_config['contest_id']
      problems = current_config['problems']
    except KeyError as e:
      raise error.ConfigurationError(
          'Cannot find all required contest data in configuration files: {0}. '
          'Reinitializing the contest might solve this error.\n'.format(e))

    # Get the needed middleware tokens to request the file and check for running
    # attempts.
    try:
      get_initial_values_token = middleware_tokens['GetInitialValues']
      download_input_token = middleware_tokens['GetInputFile']
      user_status_token = middleware_tokens['GetUserStatus']
    except KeyError as e:
      raise error.ConfigurationError(
          'Cannot find {0} token in configuration file. Reinitializing the '
          'contest might solve this error.\n'.format(e))

    # Calculate the problem index and check if it is inside the range.
    problem_index = ord(problem_letter) - ord('A')
    if problem_index < 0 or problem_index >= len(problems):
      raise error.UserError(
          'Cannot find problem {0}, there are only {1} problem(s).\n'.format(
              problem_letter, len(problems)))

    # Get the problem specification and the input id from the configuration.
    problem = problems[problem_index]
    try:
      input_id = input_spec[input_type]['input_id']
    except KeyError:
      raise error.ConfigurationError(
          'Input specification for "{1}" has no input_id.\n'.format(input_type))

    # Get the data directory from the options, if not defined, get it from the
    # configuration, using './source' as the default value if not found. In the
    # same way, get the input filename format.
    data_directory = (options.data_directory or
                      current_config.get('data_directory', './source'))
    input_name_format = (options.input_name or
                         current_config.get('input_name_format',
                                            '{problem}-{input}-{id}.in'))

    # Generate the input file name using the specified format and then return.
    try:
      input_basename = input_name_format.format(
          problem=problem_letter, input=input_type, id=id)
      input_filename = os.path.normpath(os.path.join(data_directory,
                                                     input_basename))
    except KeyError as e:
      # Print error message and exit.
      raise error.ConfigurationError(
          'Invalid input name format {0}, {1} is an invalid key, only use '
          '"problem", "input" and "id".\n'.format(input_name_format, e))

    # Print message indicating that an input is going to be downloaded.
    print '-' * 79
    print '{0} input for "{1} - {2}" at "{3}"'.format(
        input_type.capitalize(), problem_letter, problem['name'],
        input_filename)
    print '-' * 79

    # Renew the cookie if the user requested a new login or the cookie has
    # expired.
    if google_login.CookieHasExpired(cookie):
      print 'Cookie has expired, logging into the Code Jam servers...'
      cookie = None
    if not cookie or options.renew_cookie:
      cookie = code_jam_login.Login(options.password)

    # Get the contest status and check if it is accepting submissions.
    contest_status = contest_manager.GetContestStatus(
        host, cookie, get_initial_values_token, contest_id)
    if not options.force and not contest_manager.CanSubmit(contest_status):
      raise error.UserError('Cannot download inputs from this contest, its not '
                            'active or in practice mode.\n')

    # Get the user status and check if it is participating or not.
    input_index = utils.GetIndexFromInputId(input_spec, input_id)
    current_user_status = user_status.GetUserStatus(
        host, cookie, user_status_token, contest_id, input_spec)
    if (contest_status == contest_manager.ACTIVE and
        current_user_status is not None):
      # Check if this problem input can be downloaded or not. An input can be
      # downloaded as long as it has not been solved yet; a non-public input
      # also cannot have wrong tries.
      problem_inputs = current_user_status.problem_inputs
      problem_input_state = problem_inputs[problem_index][input_index]
      input_public = input_spec[input_type]['public']
      can_download = problem_input_state.solved_time == -1
      if not input_public:
        can_download = can_download and problem_input_state.wrong_tries == 0
      if not options.force and not can_download:
        raise error.UserError(
            'You cannot download {0}-{1}, it is already {2}.\n'.format(
                problem_letter, input_type,
                'solved' if input_public else ('submitted and the timer '
                                               'expired')))

      # Check if there is a pending attempt for this problem input. If there is
      # no pending attempt, show warning indicating that a new timer has
      # started.
      if problem_input_state.current_attempt == -1:
        # Show a warning message to the user indicating that a new input is
        # being downloaded, including the time available to solve it.
        remaining_time = input_spec[input_type]['time_limit']
        download_message = ('You will have {0} to submit your answer for '
                            '{1}-{2}.'.format(utils.FormatHumanTime(
                                remaining_time), problem_letter, input_type))
        utils.AskConfirmationOrDie(download_message, 'Download', options.force)
        print 'Downloading new input file.'
      else:
        # Show a message to the user indicating that the same input file is
        # being downloaded, including the time left to solve it.
        remaining_time = problem_input_state.current_attempt
        print ('You still have {0} to submit your answer for {1}-{2}.'.format(
            utils.FormatHumanTime(remaining_time), problem_letter, input_type))
        print 'Redownloading previous input file.'
    else:
      print 'Downloading practice input file.'

    # Create the input downloader and request the file.
    downloader = input_downloader.InputDownloader(
        host, cookie, download_input_token, contest_id, problem['id'])
    downloader.Download(input_id, input_filename)

  except error.OptionError as e:
    parser.print_usage()
    program_basename = os.path.basename(sys.argv[0])
    sys.stderr.write('{0}: error: {1}\n'.format(program_basename, e))
    sys.exit(1)

  except error.UserError as e:
    sys.stderr.write(str(e))
    sys.exit(1)

  except error.CommandlineError as e:
    sys.stderr.write('{0}: {1}'.format(e.__class__.__name__, e))
    sys.exit(1)
Exemplo n.º 3
0
def main():
    """Main function for the output submitter script.

  This script receives three positional arguments, the problem letter, the
  input size and the submit id.
  """
    try:
        # Create an option parser and use it to parse the supplied arguments.
        program_version = 'GCJ solution submitter {0}'.format(
            constants.VERSION)
        parser = optparse.OptionParser(
            usage='%prog [options] problem input id', version=program_version)
        parser.add_option('-l',
                          '--login',
                          action='store_true',
                          dest='renew_cookie',
                          help='Ignore the stored cookie and log in again')
        parser.add_option(
            '-p',
            '--passwd',
            action='store',
            dest='password',
            help=('Password used to log in. You will be prompted for '
                  'a password if one is required and this flag is '
                  'left empty and there is no password in the '
                  'configuration files'))
        parser.add_option(
            '-f',
            '--force',
            action='store_true',
            dest='force',
            help=('Skip check to verify if there is a running timer '
                  'and there is no submission if the input is large'))
        parser.add_option('-d',
                          '--data-directory',
                          action='store',
                          dest='data_directory',
                          help=('Directory with the I/O files and main source '
                                'files [default: ./source]'))
        parser.add_option('-o',
                          '--output-name',
                          action='store',
                          dest='output_name',
                          help='Name of the file with the solution\'s output')
        parser.add_option(
            '-a',
            '--add-source',
            action='append',
            dest='extra_sources',
            help='Add EXTRA_SOURCE to the submitted source files',
            metavar='EXTRA_SOURCE')
        parser.add_option('-z',
                          '--zip-sources',
                          action='store_true',
                          dest='zip_sources',
                          help=('Put the source files into a zip file before '
                                'submitting'))
        parser.add_option(
            '--ignore-zip',
            action='store_true',
            dest='ignore_zip',
            help=('Ignore source zip files not specified directly '
                  'using the -a option'))
        parser.add_option(
            '--ignore-default-source',
            action='store_true',
            dest='ignore_def_source',
            help=('Ignore files in the default source directory, '
                  'except for those specified using the -a option'))
        parser.add_option('--gzip-content',
                          action='store_true',
                          dest='gzip_content',
                          help=('Send the output and source code using gzip '
                                'encoding (faster)'))
        parser.add_option(
            '--nogzip-content',
            action='store_false',
            dest='gzip_content',
            help=('Send the output and sources using plain encoding '
                  '(slower)'))
        parser.add_option(
            '--base_dir',
            action='store',
            dest='base_dir',
            help=('Base directory used to parametrize configuration '
                  'file paths'))
        parser.set_defaults(renew_login=False,
                            force=False,
                            gzip_content=True,
                            zip_sources=False,
                            ignore_zip=False,
                            ignore_def_source=False,
                            base_dir=os.path.dirname(
                                os.path.realpath(__file__)))
        options, args = parser.parse_args()

        # Store the script location in a runtime constant, so it can be used by
        # the library to locate the configuration files.
        constants.SetRuntimeConstant('base_dir', options.base_dir)

        # Check that the number of arguments is valid.
        if len(args) != 3:
            raise error.OptionError('need 3 positional arguments')

        # Check that the problem idenfier is valid.
        problem_letter = args[0].upper()
        if len(problem_letter) != 1 or not problem_letter.isupper():
            raise error.OptionError(
                'invalid problem {0}, must be one uppercase letter'.format(
                    problem_letter))

        # Check that the submit id is a valid identifier.
        id = args[2]
        if not re.match('^\w+$', id):
            raise error.OptionError(
                'invalid id {0}, can only have numbers, letters '
                'and underscores'.format(id))

        # Check that the contest has been initialized.
        if not contest_manager.IsInitialized():
            raise error.ConfigurationError(
                'Contest is not initialized, please initialize the contest before '
                'trying to submit output files.\n')

        # Read user and input information from the config file.
        try:
            current_config = data_manager.ReadData()
            host = current_config['host']
            user = current_config['user']
            input_spec = current_config['input_spec']
        except KeyError as e:
            raise error.ConfigurationError(
                'Cannot find all required user data in the configuration files: {0}. '
                'Please fill the missing fields in the user configuration '
                'file.\n'.format(e))

        # Read current contest information from the config file.
        try:
            middleware_tokens = current_config['middleware_tokens']
            cookie = None if options.renew_cookie else current_config['cookie']
            contest_id = current_config['contest_id']
            problems = current_config['problems']
        except KeyError as e:
            raise error.ConfigurationError(
                'Cannot find all required contest data in configuration files: {0}. '
                'Reinitializing the contest might solve this error.\n'.format(
                    e))

        # Check that the input type is valid.
        input_type = args[1].lower()
        if input_type not in input_spec:
            raise error.OptionError('invalid input type {0}, must be one of '
                                    '({1})'.format(input_type,
                                                   ','.join(input_spec)))

        # Get the needed middleware tokens to submit solutions and check for running
        # attempts.
        try:
            get_initial_values_token = middleware_tokens['GetInitialValues']
            user_status_token = middleware_tokens['GetUserStatus']
            submit_output_token = middleware_tokens['SubmitAnswer']
        except KeyError as e:
            raise error.ConfigurationError(
                'Cannot find {0} token in configuration file. Reinitializing the '
                'contest might solve this error.\n'.format(e))

        # Calculate the problem index and check if it is inside the range.
        problem_index = ord(problem_letter) - ord('A')
        if problem_index < 0 or problem_index >= len(problems):
            raise error.UserError(
                'Cannot find problem {0}, there are only {1} '
                'problem(s).\n'.format(problem_letter, len(problems)))

        # Get the problem specification and the input id from the configuration.
        problem = problems[problem_index]
        try:
            input_id = input_spec[input_type]['input_id']
        except KeyError:
            raise error.ConfigurationError(
                'Input specification for "{1}" has no '
                'input_id.\n'.format(input_type))

        # Get the data directory from the options, if not defined, get it from the
        # configuration, using './source' as the default value if not found. In the
        # same way, get the output filename format and the main source code filename
        # format.
        data_directory = (options.data_directory
                          or current_config.get('data_directory', './source'))
        output_name_format = (options.output_name or current_config.get(
            'output_name_format', '{problem}-{input}-{id}.out'))
        source_names_format = current_config.get('source_names_format')

        # There is no sensible default for the main source, so exit with error if no
        # value is found and it wasn't ignored.
        if not options.ignore_def_source and source_names_format is None:
            raise error.UserError(
                'No format found for the default sources file name. Specify '
                '"source_name_format" in the configuration file or ignore it passing '
                '--ignore-default-source.\n')

        # Generate the output file name using the specified format and then return.
        try:
            output_basename = output_name_format.format(problem=problem_letter,
                                                        input=input_type,
                                                        id=id)
            output_filename = os.path.normpath(
                os.path.join(data_directory, output_basename))
        except KeyError as e:
            raise error.ConfigurationError(
                'Invalid output name format {0}, {1} is an invalid key, only use '
                '"problem", "input" and "id".\n'.format(input_name_format, e))

        # Create the list with all the source files and add the default source file
        # if it was requested.
        source_names = []
        if not options.ignore_def_source:
            try:
                # Process each source name in the source formats list.
                for source_name_format in source_names_format:
                    # Generate the source file name using the specified format and append
                    # it to the source list.
                    def_source_basename = source_name_format.format(
                        problem=problem_letter, input=input_type, id=id)
                    def_source_filename = os.path.normpath(
                        os.path.join(data_directory, def_source_basename))
                    source_names.append(def_source_filename)
            except KeyError as e:
                raise error.ConfigurationError(
                    'Invalid output name format {0}, {1} is an invalid key, only '
                    'use "problem", "input" and "id".\n'.format(
                        input_name_format, e))

        # Append any extra source file to the source list, normalizing their paths
        # for the current operative system.
        if options.extra_sources is not None:
            for extra_source_format in options.extra_sources:
                extra_source_file = extra_source_format.format(
                    problem=problem_letter, input=input_type, id=id)
                source_names.append(os.path.normpath(extra_source_file))

        # Print message indicating that an output is going to be submitted.
        print '-' * 79
        print '{0} output for "{1} - {2}" at "{3}"'.format(
            input_type.capitalize(), problem_letter, problem['name'],
            output_filename)
        print '-' * 79

        # Renew the cookie if the user requested a new login or the cookie has
        # expired.
        if google_login.CookieHasExpired(cookie):
            print 'Cookie has expired, logging into the Code Jam servers...'
            cookie = None
        if not cookie or options.renew_cookie:
            cookie = code_jam_login.Login(options.password)

        # Get the contest status and check if it is accepting submissions.
        contest_status = contest_manager.GetContestStatus(
            host, cookie, get_initial_values_token, contest_id)
        if not options.force and not contest_manager.CanSubmit(contest_status):
            raise error.UserError(
                'Cannot submit solutions to this contest, its not '
                'active or in practice mode.\n')

        # All problem inputs have public answers in practice mode.
        input_public = (input_spec[input_type]['public']
                        or contest_status == contest_manager.PRACTICE)

        # Get the user status and check if it is participating or not.
        input_index = utils.GetIndexFromInputId(input_spec, input_id)
        current_user_status = user_status.GetUserStatus(
            host, cookie, user_status_token, contest_id, input_spec)
        if (contest_status == contest_manager.ACTIVE
                and current_user_status is not None):
            # Check that there is a running timer for this problem input.
            problem_inputs = current_user_status.problem_inputs
            problem_input_state = problem_inputs[problem_index][input_index]
            if not options.force and problem_input_state.current_attempt == -1:
                raise error.UserError(
                    'You cannot submit {0}-{1}, the timer expired or you did not '
                    'download this input.\n'.format(problem_letter,
                                                    input_type))

            # Ask for confirmation if user is trying to resubmit a non-public output.
            if not input_public and problem_input_state.submitted:
                submit_message = (
                    'You already have submitted an output for {0}-{1}. '
                    'Resubmitting will override the previous one.'.format(
                        problem_letter, input_type))
                utils.AskConfirmationOrDie(submit_message, 'Submit',
                                           options.force)
                print 'Submitting new output and source files.'
            else:
                print 'Submitting output and source files.'
        else:
            print 'Submitting practice output and source files.'

        # Check if the contest is running and no source file is being included, show
        # a warning to the user indicating that he/she might be disqualified because
        # of this. This confirmation cannot be skipped by using --force.
        if contest_status == contest_manager.ACTIVE and not source_names:
            submit_message = (
                'You did not include source code files for this '
                'attempt. Submitting output files without source code '
                'can lead to disqualification.'.format(problem_letter,
                                                       input_type))
            utils.AskConfirmationOrDie(submit_message, 'Are you sure', False)
            print 'Submitting without source files.'

        # Create the output submitter and send the files.
        submitter = output_submitter.OutputSubmitter(host, cookie,
                                                     submit_output_token,
                                                     contest_id, problem['id'])
        submitter.Submit(input_id,
                         output_filename,
                         source_names,
                         input_public,
                         gzip_body=options.gzip_content,
                         zip_sources=options.zip_sources,
                         add_ignored_zips=not options.ignore_zip)

    except error.OptionError as e:
        parser.print_usage()
        program_basename = os.path.basename(sys.argv[0])
        sys.stderr.write('{0}: error: {1}\n'.format(program_basename, e))
        sys.exit(1)

    except error.UserError as e:
        sys.stderr.write(str(e))
        sys.exit(1)

    except error.CommandlineError as e:
        sys.stderr.write('{0}: {1}'.format(e.__class__.__name__, e))
        sys.exit(1)