def get_problem_from_url(problem_url: str) -> Problem: dummy_alphabet = 'Z' # it's impossible to reconstruct the alphabet from URL result = urllib.parse.urlparse(problem_url) normpath = os.path.normpath(result.path) normpath = normpath.replace("\\", "/") # for windows # old-style (e.g. http://agc012.contest.atcoder.jp/tasks/agc012_d) dirname, basename = posixpath.split(normpath) if result.scheme in ('', 'http', 'https') \ and result.netloc.count('.') == 3 \ and result.netloc.endswith('.contest.atcoder.jp') \ and result.netloc.split('.')[0] \ and dirname == '/tasks' \ and basename: contest_id = result.netloc.split('.')[0] problem_id = basename return Problem(Contest(contest_id), dummy_alphabet, problem_id) # new-style (e.g. https://beta.atcoder.jp/contests/abc073/tasks/abc073_a) m = re.match(r'^/contests/([\w\-_]+)/tasks/([\w\-_]+)$', normpath) if result.scheme in ('', 'http', 'https') \ and result.netloc in ('atcoder.jp', 'beta.atcoder.jp') \ and m: contest_id = m.group(1) problem_id = m.group(2) return Problem(Contest(contest_id), dummy_alphabet, problem_id) raise UnknownProblemURLError
def submit_source_code(self, contest: Contest, problem: Problem, lang: str, source: str) -> Submission: resp = self._request(contest.get_submit_url()) soup = BeautifulSoup(resp.text, "html.parser") session_id = soup.find("input", attrs={"type": "hidden"}).get("value") task_select_area = soup.find('select', attrs={"id": "submit-task-selector"}) task_field_name = task_select_area.get("name") task_number = task_select_area.find( "option", text=re.compile('{} -'.format( problem.get_alphabet()))).get("value") language_select_area = soup.find( 'select', attrs={"id": "submit-language-selector-{}".format(task_number)}) language_field_name = language_select_area.get("name") language_number = language_select_area.find("option", text=lang).get("value") postdata = { "__session": session_id, task_field_name: task_number, language_field_name: language_number, "source_code": source } resp = self._request(contest.get_submit_url(), data=postdata, method='POST') return Submission.make_submissions_from(resp.text)[0]
def test_url_parser(self): problem = Problem(Contest("utpc2014"), "Z", "utpc2014_k") urls = [ "http://utpc2014.contest.atcoder.jp/tasks/utpc2014_k", "http://beta.atcoder.jp/contests/utpc2014/tasks/utpc2014_k", "http://atcoder.jp/contests/utpc2014/tasks/utpc2014_k", "https://utpc2014.contest.atcoder.jp/tasks/utpc2014_k", "https://beta.atcoder.jp/contests/utpc2014/tasks/utpc2014_k", "https://atcoder.jp/contests/utpc2014/tasks/utpc2014_k", "https://atcoder.jp/contests/utpc2014/tasks/utpc2014_k?lang=en", "https://atcoder.jp/contests/utpc2014/tasks/utpc2014_k/?lang=en", ] for url in urls: self.assertEqual( get_problem_from_url(url).to_dict(), problem.to_dict())
def download_problem_content(self, problem: Problem) -> ProblemContent: resp = self._request(problem.get_url()) try: return ProblemContent.from_html(resp.text) except (InputFormatDetectionError, SampleDetectionError) as e: raise e
def submit_source_code(self, contest: Contest, problem: Problem, lang: Union[str, Language], source: str) -> Submission: if isinstance(lang, str): warnings.warn( "Parameter lang as a str object is deprecated. " "Please use 'atcodertools.common.language.Language' object instead", UserWarning) lang_option_pattern = lang else: lang_option_pattern = lang.submission_lang_pattern resp = self._request(contest.get_submit_url()) soup = BeautifulSoup(resp.text, "html.parser") session_id = soup.find("input", attrs={"type": "hidden"}).get("value") task_select_area = soup.find( 'select', attrs={"id": "submit-task-selector"}) task_field_name = task_select_area.get("name") task_number = task_select_area.find( "option", text=re.compile('{} -'.format(problem.get_alphabet()))).get("value") language_select_area = soup.find( 'select', attrs={"id": "submit-language-selector-{}".format(task_number)}) language_field_name = language_select_area.get("name") language_number = language_select_area.find( "option", text=lang_option_pattern).get("value") postdata = { "__session": session_id, task_field_name: task_number, language_field_name: language_number, "source_code": source } resp = self._request( contest.get_submit_url(), data=postdata, method='POST') return Submission.make_submissions_from(resp.text)[0]
def from_dict(cls, dic): return Metadata( problem=Problem.from_dict(dic["problem"]), code_filename=dic["code_filename"], sample_in_pattern=dic["sample_in_pattern"], sample_out_pattern=dic["sample_out_pattern"], lang=Language.from_name(dic["lang"]), )
def test_url_parser(self): dummy_alphabet = "Z" problem = Problem(Contest("utpc2014"), "Z", "utpc2014_k") urls = [ "http://utpc2014.contest.atcoder.jp/tasks/utpc2014_k", "http://beta.atcoder.jp/contests/utpc2014/tasks/utpc2014_k", "http://atcoder.jp/contests/utpc2014/tasks/utpc2014_k", "https://utpc2014.contest.atcoder.jp/tasks/utpc2014_k", "https://beta.atcoder.jp/contests/utpc2014/tasks/utpc2014_k", "https://atcoder.jp/contests/utpc2014/tasks/utpc2014_k", "https://atcoder.jp/contests/utpc2014/tasks/utpc2014_k?lang=en", "https://atcoder.jp/contests/utpc2014/tasks/utpc2014_k/?lang=en", ] for url in urls: self.assertEqual( get_problem_from_url(url).to_dict(), problem.to_dict())
def download_problem_list(self, contest: Contest) -> List[Problem]: resp = self._request(contest.get_problem_list_url()) soup = BeautifulSoup(resp.text, "html.parser") res = [] for tag in soup.select('.linkwrapper')[0::2]: alphabet = tag.text problem_id = tag.get("href").split("/")[-1] res.append(Problem(contest, alphabet, problem_id)) return res
def test_submit_source_code(self): contest = Contest("arc001") problem = Problem(contest, "A", "arc001_1") self.client._request = create_fake_request_func( {contest.get_submit_url(): fake_resp("submit/after_get.html")}, {contest.get_submit_url(): fake_resp("submit/after_post.html")}) submission = self.client.submit_source_code(contest, problem, "C++14 (GCC 5.4.1)", "x") self.assertEqual(3905485, submission.submission_id) self.assertEqual("arc001_1", submission.problem_id)
def test_submit_source_code(self): contest = Contest("arc001") problem = Problem(contest, "A", "arc001_1") self.client._request = create_fake_request_func( {contest.get_submit_url(): fake_resp("submit/after_get.html")}, {contest.get_submit_url(): fake_resp("submit/after_post.html")}) # test two patterns (str, Language object) for parameter lang for lang in [CPP, "C++14 (GCC 5.4.1)"]: submission = self.client.submit_source_code( contest, problem, lang, "x") self.assertEqual(3905485, submission.submission_id) self.assertEqual("arc001_1", submission.problem_id)
def from_dict(cls, dic): if "judge" in dic: judge_type = dic["judge"]["judge_type"] if judge_type == "normal": judge_method = NormalJudge.from_dict(dic["judge"]) elif judge_type == "decimal": judge_method = DecimalJudge.from_dict(dic["judge"]) else: raise Exception("invalid judge type") else: judge_method = NormalJudge() return Metadata(problem=Problem.from_dict(dic["problem"]), code_filename=dic["code_filename"], sample_in_pattern=dic["sample_in_pattern"], sample_out_pattern=dic["sample_out_pattern"], lang=Language.from_name(dic["lang"]), judge_method=judge_method)
def from_dict(cls, dic): if "judge" in dic: judge_type = dic["judge"]["judge_type"] if judge_type == "normal": judge_method = NormalJudge.from_dict(dic["judge"]) elif judge_type == "decimal": judge_method = DecimalJudge.from_dict(dic["judge"]) elif judge_type == "multisolution": judge_method = MultiSolutionJudge() elif judge_type == "interactive": judge_method = InteractiveJudge() else: raise NoJudgeTypeException() else: judge_method = NormalJudge() return Metadata(problem=Problem.from_dict(dic["problem"]), code_filename=dic["code_filename"], sample_in_pattern=dic["sample_in_pattern"], sample_out_pattern=dic["sample_out_pattern"], lang=Language.from_name(dic["lang"]), judge_method=judge_method)
def prepare_procedure(atcoder_client: AtCoderClient, problem: Problem, config: Config): workspace_root_path = config.code_style_config.workspace_dir template_code_path = config.code_style_config.template_file lang = config.code_style_config.lang pid = problem.get_alphabet() problem_dir_path = os.path.join(workspace_root_path, problem.get_contest().get_id(), pid) def emit_error(text): logger.error(with_color("Problem {}: {}".format(pid, text), Fore.RED)) def emit_warning(text): logger.warning("Problem {}: {}".format(pid, text)) def emit_info(text): logger.info("Problem {}: {}".format(pid, text)) emit_info('{} is used for template'.format(template_code_path)) original_html = atcoder_client.download_problem_content_raw_html(problem) constants = predict_constants(original_html) if constants.judge_method.judge_type != JudgeType.Interactive: # Fetch problem data from the statement try: content = get_problem_content(original_html) except InputFormatDetectionError as e: emit_error("Failed to download input format.") raise e except SampleDetectionError as e: emit_error("Failed to download samples.") raise e # Store examples to the directory path if len(content.get_samples()) == 0: emit_info("No samples.") else: os.makedirs(problem_dir_path, exist_ok=True) create_examples(content.get_samples(), problem_dir_path, config.etc_config.in_example_format, config.etc_config.out_example_format) emit_info("Created examples.") code_file_path = os.path.join(problem_dir_path, "main.{}".format(lang.extension)) # If there is an existing code, just create backup if os.path.exists(code_file_path): backup_id = 1 while True: backup_name = "{}.{}".format(code_file_path, backup_id) if not os.path.exists(backup_name): new_path = backup_name shutil.copy(code_file_path, backup_name) break backup_id += 1 emit_info("Backup for existing code '{}' -> '{}'".format( code_file_path, new_path)) if constants.judge_method.judge_type != JudgeType.Interactive: try: prediction_result = predict_format(content) emit_info( with_color("Format prediction succeeded", Fore.LIGHTGREEN_EX)) except (NoPredictionResultError, MultiplePredictionResultsError) as e: prediction_result = FormatPredictionResult.empty_result() if isinstance(e, NoPredictionResultError): msg = "No prediction -- Failed to understand the input format" else: msg = "Too many prediction -- Failed to understand the input format" emit_warning(with_color(msg, Fore.LIGHTRED_EX)) else: prediction_result = FormatPredictionResult.empty_result() code_generator = config.code_style_config.code_generator with open(template_code_path, "r") as f: template = f.read() create_code( code_generator( CodeGenArgs(template, prediction_result.format, constants, config.code_style_config)), code_file_path) emit_info("Saved code to {}".format(code_file_path)) # Save metadata metadata_path = os.path.join(problem_dir_path, "metadata.json") Metadata( problem, os.path.basename(code_file_path), config.etc_config.in_example_format.replace("{}", "*"), config.etc_config.out_example_format.replace("{}", "*"), lang, constants.judge_method, ).save_to(metadata_path) emit_info("Saved metadata to {}".format(metadata_path)) if config.postprocess_config.exec_cmd_on_problem_dir is not None: emit_info( _message_on_execution( problem_dir_path, config.postprocess_config.exec_cmd_on_problem_dir)) config.postprocess_config.execute_on_problem_dir(problem_dir_path) output_splitter()
def download_problem_content_raw_html(self, problem: Problem) -> str: resp = self._request(problem.get_url()) return resp.text
def test_download_problem_content(self): content = self.client.download_problem_content( Problem(Contest("arc002"), "C", "arc002_3")) self.assertEqual("N\nc_{1}c_{2}...c_{N}\n", content.input_format_text) self.assertEqual(3, len(content.samples))
def prepare_procedure(atcoder_client: AtCoderClient, problem: Problem, config: Config): workspace_root_path = config.code_style_config.workspace_dir template_code_path = config.code_style_config.template_file lang = config.code_style_config.lang pid = problem.get_alphabet() problem_dir_path = os.path.join( workspace_root_path, problem.get_contest().get_id(), pid) def emit_error(text): logging.error(with_color("Problem {}: {}".format(pid, text), Fore.RED)) def emit_warning(text): logging.warning("Problem {}: {}".format(pid, text)) def emit_info(text): logging.info("Problem {}: {}".format(pid, text)) emit_info('{} is used for template'.format(template_code_path)) # Fetch problem data from the statement try: content = atcoder_client.download_problem_content(problem) except InputFormatDetectionError as e: emit_error("Failed to download input format.") raise e except SampleDetectionError as e: emit_error("Failed to download samples.") raise e # Store examples to the directory path if len(content.get_samples()) == 0: emit_info("No samples.") else: os.makedirs(problem_dir_path, exist_ok=True) create_examples(content.get_samples(), problem_dir_path, IN_EXAMPLE_FORMAT, OUT_EXAMPLE_FORMAT) emit_info("Created examples.") code_file_path = os.path.join( problem_dir_path, "main.{}".format(lang.extension)) # If there is an existing code, just create backup if os.path.exists(code_file_path): backup_id = 1 while True: backup_name = "{}.{}".format(code_file_path, backup_id) if not os.path.exists(backup_name): new_path = backup_name shutil.copy(code_file_path, backup_name) break backup_id += 1 emit_info( "Backup for existing code '{}' -> '{}'".format( code_file_path, new_path)) try: prediction_result = predict_format(content) emit_info( with_color("Format prediction succeeded", Fore.LIGHTGREEN_EX)) except (NoPredictionResultError, MultiplePredictionResultsError) as e: prediction_result = FormatPredictionResult.empty_result() if isinstance(e, NoPredictionResultError): msg = "No prediction -- Failed to understand the input format" else: msg = "Too many prediction -- Failed to understand the input format" emit_warning(with_color(msg, Fore.LIGHTRED_EX)) constants = predict_constants(content.original_html) code_generator = config.code_style_config.code_generator with open(template_code_path, "r") as f: template = f.read() create_code(code_generator( CodeGenArgs( template, prediction_result.format, constants, config.code_style_config )), code_file_path) emit_info("Saved code to {}".format(code_file_path)) # Save metadata metadata_path = os.path.join(problem_dir_path, "metadata.json") Metadata(problem, os.path.basename(code_file_path), IN_EXAMPLE_FORMAT.replace("{}", "*"), OUT_EXAMPLE_FORMAT.replace("{}", "*"), lang, ).save_to(metadata_path) emit_info("Saved metadata to {}".format(metadata_path)) if config.postprocess_config.exec_cmd_on_problem_dir is not None: emit_info(_message_on_execution(problem_dir_path, config.postprocess_config.exec_cmd_on_problem_dir)) config.postprocess_config.execute_on_problem_dir( problem_dir_path) output_splitter()