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)
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)