def Initialize(tournament_id, contest_id, password=None): """Initialize configuration for the specified tournament or contest. This function initializes the tool for a contest. If the contest is None, the tool will be initialized for the current contest of the specified tournament. Either one of tournament_id or contest_id must be not None. The retrieved data is stored in the current configuration file. Args: tournament_id: ID of the tournament whose current contest must be initialized. contest_id: ID of the contest to initialize. If None, the server will ask for the current contest of the specified tournament. password: Password specified by the user, if any. Raises: error.ConfigurationError: If the contest data is invalid or incomplete. error.UserError: If no contest was specified and there is no running contest for the specified tournament. """ # Reset the current configuration file with the one provided by the user and # renew the cookie, so the middleware tokens are retrieved correctly. try: user_config_path = data_manager.ParametrizeConfigPath( constants.USER_CONFIG_PATH) current_config_path = data_manager.ParametrizeConfigPath( constants.CURRENT_CONFIG_PATH) shutil.copy(user_config_path, current_config_path) code_jam_login.Login(password) except OSError as e: raise error.InternalError( 'Configuration file {0} could not be created: ' '{1}.\n'.format(current_config_path, e)) # Read the current configuration file and extract the host and the cookie. try: contest_data = data_manager.ReadData() host = contest_data['host'] cookie = contest_data['cookie'] except KeyError as e: # Indicate that no host or cookie was configured and exit with error. raise error.ConfigurationError('No host or login cookie found in the ' 'configuration file: {0}.\n'.format(e)) # Get the current contest if no contest id was specified. If there is no # running contest show an error to the user. if contest_id is None: contest_id = GetCurrentContestId(host, cookie, tournament_id) if contest_id is None: raise error.UserError( 'No contest is running for tournament %s and no ' 'contest id was specified.\n' % tournament_id) sys.stdout.write( 'Initializing tool for current contest with id %s.\n' % contest_id) # Retrieve the problem list and validate the extracted contest data and exit # if there is any error. problems = _GetProblems(host, cookie, contest_id) _ValidateContestDataOrRaise(contest_data['middleware_tokens'], problems) # Add the contest id, the problems and the middleware tokens to the contest # data. contest_data['contest_id'] = contest_id contest_data['problems'] = problems # Finally, write the contest data to the current data file, and then # renew the cookie stored in the configuration. data_manager.WriteData(contest_data) sys.stdout.write('Contest {0} initialized successfully, {1} problem(s) ' 'retrieved.\n'.format(contest_id, len(problems)))
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)
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)