def post(self, task_name, submission_num): task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) submission = self.get_submission(task, submission_num) if submission is None: raise tornado_web.HTTPError(404) try: accept_token(self.sql_session, submission, self.timestamp) self.sql_session.commit() except UnacceptableToken as e: self.notify_error(e.subject, e.text) except TokenAlreadyPlayed as e: self.notify_warning(e.subject, e.text) else: # Inform ProxyService and eventually the ranking that the # token has been played. self.service.proxy_service.submission_tokened( submission_id=submission.id) logger.info("Token played by user %s on task %s.", self.current_user.user.username, task.name) # Add "All ok" notification. self.notify_success(N_("Token request received"), N_("Your request has been received " "and applied to the submission.")) self.redirect(self.contest_url("tasks", task.name, "submissions"))
def get(self, task_name, submission_num): task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) submission = self.get_submission(task, submission_num) if submission is None: raise tornado_web.HTTPError(404) sr = submission.get_result(task.active_dataset) score_type = task.active_dataset.score_type_object details = None if sr is not None and sr.scored(): # During analysis mode we show the full feedback regardless of # what the task says. is_analysis_mode = self.r_params["actual_phase"] == 3 if submission.is_unit_test(): raw_details = sr.unit_test_score_details elif score_type.feedback() == "full" or submission.tokened() or \ is_analysis_mode: raw_details = sr.score_details else: raw_details = sr.public_score_details if is_analysis_mode: feedback_level = FEEDBACK_LEVEL_FULL else: feedback_level = task.feedback_level details = score_type.get_html_details( raw_details, feedback_level, translation=self.translation) self.render("submission_details.html", sr=sr, details=details, **self.r_params)
def post(self): if not self.contest.allow_registration: raise tornado_web.HTTPError(404) create_new_user = self.get_argument("new_user") == "true" # Get or create user if create_new_user: user = self._create_user() else: user = self._get_user() # Check if the participation exists contest = self.contest tot_participants = self.sql_session.query(Participation)\ .filter(Participation.user == user)\ .filter(Participation.contest == contest)\ .count() if tot_participants > 0: raise tornado_web.HTTPError(409) # Create participation team = self._get_team() participation = Participation(user=user, contest=self.contest, team=team) self.sql_session.add(participation) self.sql_session.commit() self.finish(user.username)
def get(self, task_name, user_test_num, filename): if not self.r_params["testing_enabled"]: raise tornado_web.HTTPError(404) task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) user_test = self.get_user_test(task, user_test_num) if user_test is None: raise tornado_web.HTTPError(404) # filename is the name used by the browser, hence is something # like 'foo.c' (and the extension is CMS's preferred extension # for the language). To retrieve the right file, we need to # decode it to 'foo.%l'. stored_filename = filename if user_test.language is not None: extension = get_language(user_test.language).source_extension stored_filename = re.sub(r'%s$' % extension, '.%l', filename) if stored_filename in user_test.files: digest = user_test.files[stored_filename].digest elif stored_filename in user_test.managers: digest = user_test.managers[stored_filename].digest else: raise tornado_web.HTTPError(404) self.sql_session.close() mimetype = get_type_for_file_name(filename) if mimetype is None: mimetype = 'application/octet-stream' self.fetch(digest, mimetype, filename)
def get(self, task_name, user_test_num, io): if not self.r_params["testing_enabled"]: raise tornado_web.HTTPError(404) task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) user_test = self.get_user_test(task, user_test_num) if user_test is None: raise tornado_web.HTTPError(404) if io == "input": digest = user_test.input else: # io == "output" tr = user_test.get_result(task.active_dataset) digest = tr.output if tr is not None else None self.sql_session.close() if digest is None: raise tornado_web.HTTPError(404) mimetype = 'text/plain' self.fetch(digest, mimetype, io)
def post(self, contest_id, question_id): ref = self.url("contest", contest_id, "questions") question = self.safe_get_item(Question, question_id) self.contest = self.safe_get_item(Contest, contest_id) # Protect against URLs providing incompatible parameters. if self.contest is not question.participation.contest: raise tornado_web.HTTPError(404) # Can claim/unclaim only a question not ignored or answered. if question.ignored or question.reply_timestamp is not None: raise tornado_web.HTTPError(405) should_claim = self.get_argument("claim", "no") == "yes" # Commit the change. if should_claim: question.admin = self.current_user else: question.admin = None if self.try_commit(): logger.info( "Question '%s' by user %s in contest %s has " "been %s by %s", question.subject, question.participation.user.username, question.participation.contest.name, "claimed" if should_claim else "unclaimed", self.current_user.name) self.redirect(ref)
def get(self, task_name, submission_num): task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) submission = self.get_submission(task, submission_num) if submission is None: raise tornado_web.HTTPError(404) sr = submission.get_result(task.active_dataset) data = {} if sr is None: # implicit compiling state while result is not created data["status"] = SubmissionResult.COMPILING else: data["status"] = sr.get_status() data["status_text"] = self._(self.STATUS_TEXT[data["status"]]) # For terminal statuses we add the scores information to the payload. if data["status"] == SubmissionResult.COMPILATION_FAILED \ or data["status"] == SubmissionResult.SCORED: self.add_task_score(submission.participation, task, data) score_type = task.active_dataset.score_type_object if True: data["max_public_score"] = \ round(score_type.max_public_score, task.score_precision) if data["status"] == SubmissionResult.SCORED: data["public_score"] = \ round(sr.public_score, task.score_precision) data["public_score_message"] = score_type.format_score( sr.public_score, score_type.max_public_score, sr.public_score_details, task.score_precision, translation=self.translation) if submission.token is not None or \ self.r_params["actual_phase"] == 3 or \ score_type.feedback() == "full" or \ submission.is_unit_test(): data["max_score"] = \ round(score_type.max_score, task.score_precision) if data["status"] == SubmissionResult.SCORED: data["score"] = \ round(sr.score, task.score_precision) data["score_message"] = score_type.format_score( sr.score, score_type.max_score, sr.score_details, task.score_precision, translation=self.translation) if submission.is_unit_test(): utd = sr.unit_test_score_details data["verdict"] = utd["verdict"] data["comment"] = submission.comment self.write(data)
def _create_user(self): try: first_name = self.get_argument("first_name") last_name = self.get_argument("last_name") username = self.get_argument("username") password = self.get_argument("password") email = self.get_argument("email") if len(email) == 0: email = None if self.contest.registration_requires_captcha: captcha_input = self.get_argument("captcha") captcha_input_signature = self.signature(captcha_input) captcha_cookie = self.get_secure_cookie("captcha").decode( 'utf-8') captcha_clear_signature, captcha_username = captcha_cookie.split( '_', 1) if not 1 <= len(first_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(last_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(username) <= self.MAX_INPUT_LENGTH: raise ValueError() if not re.match(r"^[A-Za-z0-9_-]+$", username): raise ValueError() if not self.MIN_PASSWORD_LENGTH <= len(password) \ <= self.MAX_INPUT_LENGTH: raise ValueError() if self.contest.registration_requires_captcha: if not re.match(r"^[0-9]+$", captcha_input): raise ValueError() if not captcha_input_signature == captcha_clear_signature: raise ValueError() if not username == captcha_username: raise ValueError() except (tornado_web.MissingArgumentError, ValueError): raise tornado_web.HTTPError(400) # Override password with its hash password = hash_password(password) # Check if the username is available tot_users = self.sql_session.query(User)\ .filter(User.username == username).count() if tot_users != 0: # HTTP 409: Conflict raise tornado_web.HTTPError(409) # Store new user user = User(first_name, last_name, username, password, email=email) self.sql_session.add(user) return user
def post(self, contest_id, question_id): ref = self.url("contest", contest_id, "questions") question = self.safe_get_item(Question, question_id) self.contest = self.safe_get_item(Contest, contest_id) # Protect against URLs providing incompatible parameters. if self.contest is not question.participation.contest: raise tornado_web.HTTPError(404) reply_subject_code = self.get_argument("reply_question_quick_answer", "") question.reply_text = self.get_argument("reply_question_text", "") # Ignore invalid answers if reply_subject_code not in QuestionReplyHandler.QUICK_ANSWERS: question.reply_subject = "" else: # Quick answer given, ignore long answer. question.reply_subject = \ QuestionReplyHandler.QUICK_ANSWERS[reply_subject_code] question.reply_text = "" question.last_action = make_datetime() question.reply_timestamp = question.last_action question.reply_source = "web" question.ignored = False question.admin = self.current_user if self.try_commit(): logger.info( "Reply sent to user %s in contest %s for " "question with id %s.", question.participation.user.username, question.participation.contest.name, question_id) self.redirect(ref)
def choose_contest(self): """Fill self.contest using contest passed as argument or path. If a contest was specified as argument to CWS, fill self.contest with that; otherwise extract it from the URL path. """ if self.is_multi_contest(): # Choose the contest found in the path argument # see: https://github.com/tornadoweb/tornado/issues/1673 contest_name = self.path_args[0] # Select the correct contest or return an error self.contest = self.sql_session.query(Contest)\ .filter(Contest.name == contest_name).first() if self.contest is None: self.contest = Contest(name=contest_name, description=contest_name) # render_params in this class assumes the contest is loaded, # so we cannot call it without a fully defined contest. Luckily # the one from the base class is enough to display a 404 page. super().prepare() self.r_params = super().render_params() raise tornado_web.HTTPError(404) else: # Select the contest specified on the command line self.contest = Contest.get_from_id(self.service.contest_id, self.sql_session)
def get(self, task_name, user_test_num): if not self.r_params["testing_enabled"]: raise tornado_web.HTTPError(404) task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) user_test = self.get_user_test(task, user_test_num) if user_test is None: raise tornado_web.HTTPError(404) tr = user_test.get_result(task.active_dataset) self.render("user_test_details.html", task=task, tr=tr, **self.r_params)
def get(self, task_name, filename): task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) if filename not in task.attachments: raise tornado_web.HTTPError(404) attachment = task.attachments[filename].digest self.sql_session.close() mimetype = get_type_for_file_name(filename) if mimetype is None: mimetype = 'application/octet-stream' self.fetch(attachment, mimetype, filename)
def post(self, task_name): task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) # Only set the official bit when the user can compete and we are not in # analysis mode. official = self.r_params["actual_phase"] == 0 query_args = dict() try: submission = accept_submission( self.sql_session, self.service.file_cacher, self.current_user, task, self.timestamp, self.request.files, self.get_argument("language", None), official) self.sql_session.commit() except UnacceptableSubmission as e: logger.info("Sent error: `%s' - `%s'", e.subject, e.formatted_text) self.notify_error(e.subject, e.text, e.text_params) else: self.service.evaluation_service.new_submission( submission_id=submission.id) self.notify_success(N_("Submission received"), N_("Your submission has been received " "and is currently being evaluated.")) # The argument (encrypted submission id) is not used by CWS # (nor it discloses information to the user), but it is # useful for automatic testing to obtain the submission id). query_args["submission_id"] = \ encrypt_number(submission.id, config.secret_key) self.redirect(self.contest_url("tasks", task.name, "submissions", **query_args))
def post(self, contest_id, question_id): ref = self.url("contest", contest_id, "questions") question = self.safe_get_item(Question, question_id) self.contest = self.safe_get_item(Contest, contest_id) # Protect against URLs providing incompatible parameters. if self.contest is not question.participation.contest: raise tornado_web.HTTPError(404) should_ignore = self.get_argument("ignore", "no") == "yes" # Commit the change. question.ignored = should_ignore question.last_action = make_datetime() question.reply_source = "web" question.reply_subject = None question.reply_text = None question.admin = self.current_user if self.try_commit(): logger.info( "Question '%s' by user %s in contest %s has " "been %s", question.subject, question.participation.user.username, question.participation.contest.name, "ignored" if should_ignore else "unignored") self.redirect(ref)
def _get_user(self): username = self.get_argument("username") password = self.get_argument("password") # Find user if it exists user = self.sql_session.query(User)\ .filter(User.username == username)\ .first() if user is None: raise tornado_web.HTTPError(404) # Check if password is correct if not validate_password(user.password, password): raise tornado_web.HTTPError(403) return user
def get(self): this_captcha = self.captcha.captcha() captcha_clear = ''.join([str(x) for x in this_captcha[0]]) captcha_bs = this_captcha[1] mimetype = get_type_for_file_name("captcha.png") if mimetype is None: mimetype = 'application/octet-stream' self.add_header('Content-Type', mimetype) #We require an identifier so a captcha's cookie is restricted #in use for a specific action. try: identifier = self.get_argument("identifier") if not 1 <= len(identifier) <= self.MAX_INPUT_LENGTH: raise ValueError() if not re.match(r"^[A-Za-z0-9_-]+$", identifier): raise ValueError() except (tornado_web.MissingArgumentError, ValueError): raise tornado_web.HTTPError(400) #We don't use a reference to the running contest, so the answer #to a captcha in one contest could be used for the same action in #another. Won't fix #identifier should be signed so it can't be tempered with, which is taken #care of by set_secure_cookie. #captcha_clear should additionally not be accessible for the user, #so we only include its signature and compare that later to the signature #of the user's input cookie = self.signature(captcha_clear) + "_" + identifier cookie_name = "captcha" self.set_secure_cookie(cookie_name, cookie, expires_days=None) self.write(captcha_bs)
def get(self, task_name, lang_code): task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) if lang_code not in task.statements: raise tornado_web.HTTPError(404) statement = task.statements[lang_code].digest self.sql_session.close() if len(lang_code) > 0: filename = "%s (%s).pdf" % (task.name, lang_code) else: filename = "%s.pdf" % task.name self.fetch(statement, "application/pdf", filename)
def get(self, task_name, user_test_num): if not self.r_params["testing_enabled"]: raise tornado_web.HTTPError(404) task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) user_test = self.get_user_test(task, user_test_num) if user_test is None: raise tornado_web.HTTPError(404) ur = user_test.get_result(task.active_dataset) data = dict() if ur is None: data["status"] = UserTestResult.COMPILING else: data["status"] = ur.get_status() if data["status"] == UserTestResult.COMPILING: data["status_text"] = self._("Compiling...") elif data["status"] == UserTestResult.COMPILATION_FAILED: data["status_text"] = "%s <a class=\"details\">%s</a>" % ( self._("Compilation failed"), self._("details")) elif data["status"] == UserTestResult.EVALUATING: data["status_text"] = self._("Executing...") elif data["status"] == UserTestResult.EVALUATED: data["status_text"] = "%s <a class=\"details\">%s</a>" % ( self._("Executed"), self._("details")) if ur.execution_time is not None: data["execution_time"] = \ self.translation.format_duration(ur.execution_time) else: data["execution_time"] = None if ur.execution_memory is not None: data["memory"] = \ self.translation.format_size(ur.execution_memory) else: data["memory"] = None data["output"] = ur.output is not None self.write(data)
def get(self): if not self.contest.allow_registration: raise tornado_web.HTTPError(404) self.r_params["MAX_INPUT_LENGTH"] = self.MAX_INPUT_LENGTH self.r_params["MIN_PASSWORD_LENGTH"] = self.MIN_PASSWORD_LENGTH self.r_params["teams"] = self.sql_session.query(Team)\ .order_by(Team.name).all() self.render("register.html", **self.r_params)
def delete(self, contest_id, ann_id): ann = self.safe_get_item(Announcement, ann_id) self.contest = self.safe_get_item(Contest, contest_id) # Protect against URLs providing incompatible parameters. if self.contest is not ann.contest: raise tornado_web.HTTPError(404) self.sql_session.delete(ann) self.try_commit() # Page to redirect to. self.write("announcements")
def delete(self, task_id, attachment_id): attachment = self.safe_get_item(Attachment, attachment_id) task = self.safe_get_item(Task, task_id) # Protect against URLs providing incompatible parameters. if attachment.task is not task: raise tornado_web.HTTPError(404) self.sql_session.delete(attachment) self.try_commit() # Page to redirect to. self.write("%s" % task.id)
def _create_user(self): try: first_name = self.get_argument("first_name") last_name = self.get_argument("last_name") username = self.get_argument("username") password = self.get_argument("password") email = self.get_argument("email") if len(email) == 0: email = None if not 1 <= len(first_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(last_name) <= self.MAX_INPUT_LENGTH: raise ValueError() if not 1 <= len(username) <= self.MAX_INPUT_LENGTH: raise ValueError() if not re.match(r"^[A-Za-z0-9_-]+$", username): raise ValueError() if not self.MIN_PASSWORD_LENGTH <= len(password) \ <= self.MAX_INPUT_LENGTH: raise ValueError() except (tornado_web.MissingArgumentError, ValueError): raise tornado_web.HTTPError(400) # Override password with its hash password = hash_password(password) # Check if the username is available tot_users = self.sql_session.query(User)\ .filter(User.username == username).count() if tot_users != 0: # HTTP 409: Conflict raise tornado_web.HTTPError(409) # Store new user user = User(first_name, last_name, username, password, email=email) self.sql_session.add(user) return user
def post(self, task_name): if not self.r_params["testing_enabled"]: raise tornado_web.HTTPError(404) task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) query_args = dict() try: user_test = accept_user_test(self.sql_session, self.service.file_cacher, self.current_user, task, self.timestamp, self.request.files, self.get_argument("language", None)) self.sql_session.commit() except TestingNotAllowed: logger.warning("User %s tried to make test on task %s.", self.current_user.user.username, task_name) raise tornado_web.HTTPError(404) except UnacceptableUserTest as e: logger.info("Sent error: `%s' - `%s'", e.subject, e.formatted_text) self.notify_error(e.subject, e.text, e.text_params) else: self.service.evaluation_service.new_user_test( user_test_id=user_test.id) self.notify_success( N_("Test received"), N_("Your test has been received " "and is currently being executed.")) # The argument (encrypted user test id) is not used by CWS # (nor it discloses information to the user), but it is # useful for automatic testing to obtain the user test id). query_args["user_test_id"] = \ encrypt_number(user_test.id, config.secret_key) self.redirect( self.contest_url("testing", task_name=task.name, **query_args))
def get(self): participation = self.current_user if not self.r_params["testing_enabled"]: raise tornado_web.HTTPError(404) user_tests = dict() user_tests_left = dict() default_task = None user_tests_left_contest = None if self.contest.max_user_test_number is not None: user_test_c = \ get_submission_count(self.sql_session, participation, contest=self.contest, cls=UserTest) user_tests_left_contest = \ self.contest.max_user_test_number - user_test_c for task in self.contest.tasks: if self.get_argument("task_name", None) == task.name: default_task = task if default_task is None and task.active_dataset.task_type_object.testable: default_task = task user_tests[task.id] = self.sql_session.query(UserTest)\ .filter(UserTest.participation == participation)\ .filter(UserTest.task == task)\ .all() user_tests_left_task = None if task.max_user_test_number is not None: user_tests_left_task = \ task.max_user_test_number - len(user_tests[task.id]) user_tests_left[task.id] = user_tests_left_contest if user_tests_left_task is not None and \ (user_tests_left_contest is None or user_tests_left_contest > user_tests_left_task): user_tests_left[task.id] = user_tests_left_task # Make sure we do not show negative value if admins changed # the maximum if user_tests_left[task.id] is not None: user_tests_left[task.id] = max(0, user_tests_left[task.id]) if default_task is None and len(self.contest.tasks) > 0: default_task = self.contest.tasks[0] self.render("test_interface.html", default_task=default_task, user_tests=user_tests, user_tests_left=user_tests_left, **self.r_params)
def delete(self, dataset_id, manager_id): manager = self.safe_get_item(Manager, manager_id) dataset = self.safe_get_item(Dataset, dataset_id) # Protect against URLs providing incompatible parameters. if manager.dataset is not dataset: raise tornado_web.HTTPError(404) task_id = dataset.task_id self.sql_session.delete(manager) self.try_commit() self.write("./%d" % task_id)
def post(self, contest_id, user_id): fallback_page = \ self.url("contest", contest_id, "user", user_id, "edit") self.contest = self.safe_get_item(Contest, contest_id) participation = self.sql_session.query(Participation)\ .filter(Participation.contest_id == contest_id)\ .filter(Participation.user_id == user_id)\ .first() # Check that the participation is valid. if participation is None: raise tornado_web.HTTPError(404) try: attrs = participation.get_attrs() self.get_password(attrs, participation.password, True) self.get_ip_networks(attrs, "ip") self.get_datetime(attrs, "starting_time") self.get_timedelta_sec(attrs, "delay_time") self.get_timedelta_sec(attrs, "extra_time") self.get_bool(attrs, "hidden") self.get_bool(attrs, "unofficial") self.get_bool(attrs, "unrestricted") # Update the participation. participation.set_attrs(attrs) # Update the team self.get_string(attrs, "team") team = self.sql_session.query(Team)\ .filter(Team.code == attrs["team"])\ .first() participation.team = team group_id = self.get_argument("group_id") participation.group = self.safe_get_item(Group, group_id) except Exception as error: self.service.add_notification(make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Update the user on RWS. self.service.proxy_service.reinitialize() self.redirect(fallback_page)
def _get_team(self): # If we have teams, we assume that the 'team' field is mandatory if self.sql_session.query(Team).count() > 0: try: team_code = self.get_argument("team") team = self.sql_session.query(Team)\ .filter(Team.code == team_code)\ .one() except (tornado_web.MissingArgumentError, NoResultFound): raise tornado_web.HTTPError(400) else: team = None return team
def get(self, task_name, submission_num, filename): if not self.contest.submissions_download_allowed: raise tornado_web.HTTPError(404) task = self.get_task(task_name) if task is None: raise tornado_web.HTTPError(404) submission = self.get_submission(task, submission_num) if submission is None: raise tornado_web.HTTPError(404) # The following code assumes that submission.files is a subset # of task.submission_format. CWS will always ensure that for new # submissions, yet, if the submission_format changes during the # competition, this may not hold anymore for old submissions. # filename is the name used by the browser, hence is something # like 'foo.c' (and the extension is CMS's preferred extension # for the language). To retrieve the right file, we need to # decode it to 'foo.%l'. stored_filename = filename if submission.language is not None: extension = get_language(submission.language).source_extension stored_filename = re.sub(r'%s$' % extension, '.%l', filename) if stored_filename not in submission.files: raise tornado_web.HTTPError(404) digest = submission.files[stored_filename].digest self.sql_session.close() mimetype = get_type_for_file_name(filename) if mimetype is None: mimetype = 'application/octet-stream' self.fetch(digest, mimetype, filename)
def post(self): try: accept_question(self.sql_session, self.current_user, self.timestamp, self.get_argument("question_subject", ""), self.get_argument("question_text", "")) self.sql_session.commit() except QuestionsNotAllowed: raise tornado_web.HTTPError(404) except UnacceptableQuestion as e: self.notify_error(e.subject, e.text, e.text_params) else: self.notify_success(N_("Question received"), N_("Your question has been received, you " "will be notified when it is answered.")) self.redirect(self.contest_url("communication"))
def post(self): try: printjob = accept_print_job( self.sql_session, self.service.file_cacher, self.current_user, self.timestamp, self.request.files) self.sql_session.commit() except PrintingDisabled: raise tornado_web.HTTPError(404) except UnacceptablePrintJob as e: self.notify_error(e.subject, e.text) else: self.service.printing_service.new_printjob(printjob_id=printjob.id) self.notify_success(N_("Print job received"), N_("Your print job has been received.")) self.redirect(self.contest_url("printing"))