def test_success(self): """Verify a simple success case. Push three items in the wrong order, and recover them. """ self.assertTrue(self.queue._verify()) self.queue.push(self.item_a, PriorityQueue.PRIORITY_LOW) self.queue.push(self.item_b, PriorityQueue.PRIORITY_MEDIUM, timestamp=make_datetime(10)) self.queue.push(self.item_c, PriorityQueue.PRIORITY_MEDIUM, timestamp=make_datetime(5)) self.assertTrue(self.queue._verify()) self.assertEqual(self.queue.top().item, self.item_c) top = self.queue.pop() self.assertEqual(top.item, self.item_c) top = self.queue.pop() self.assertEqual(top.item, self.item_b) top = self.queue.pop() self.assertEqual(top.item, self.item_a) self.assertTrue(self.queue._verify()) with self.assertRaises(LookupError): self.queue.pop()
def post(self, dataset_id_to_copy): fallback_page = "/dataset/%s/clone" % dataset_id_to_copy dataset = self.safe_get_item(Dataset, dataset_id_to_copy) task = self.safe_get_item(Task, dataset.task_id) task_id = task.id try: original_dataset = self.safe_get_item(Dataset, dataset_id_to_copy) except ValueError: raise tornado.web.HTTPError(404) try: attrs = dict() self.get_string(attrs, "description") # Ensure description is unique. if any(attrs["description"] == d.description for d in task.datasets): self.application.service.add_notification( make_datetime(), "Dataset name %r is already taken." % attrs["description"], "Please choose a unique name for this dataset.", ) self.redirect(fallback_page) return self.get_time_limit(attrs, "time_limit") self.get_memory_limit(attrs, "memory_limit") self.get_task_type(attrs, "task_type", "TaskTypeOptions_") self.get_score_type(attrs, "score_type", "score_type_parameters") # Create the dataset. attrs["autojudge"] = False attrs["task"] = task dataset = Dataset(**attrs) self.sql_session.add(dataset) except Exception as error: logger.warning("Invalid field.", exc_info=True) self.application.service.add_notification(make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if original_dataset is not None: # If we were cloning the dataset, copy all managers and # testcases across too. If the user insists, clone all # evaluation information too. clone_results = bool(self.get_argument("clone_results", False)) dataset.clone_from(original_dataset, True, True, clone_results) # If the task does not yet have an active dataset, make this # one active. if task.active_dataset is None: task.active_dataset = dataset if self.try_commit(): self.redirect("/task/%s" % task_id) else: self.redirect(fallback_page)
def get_contest(self): """See docstring in class Loader. """ args = {} name = os.path.split(self.path)[1] args["description"] = args["name"] = name args["token_mode"] = "disabled" self.token_mode = {"token_mode": "disabled"} args["start"] = make_datetime(1388534400) # Beginning of 2014 year args["stop"] = make_datetime(1577836800) # Beginning of 2020 year args["per_user_time"] = timedelta(seconds=18000) # 5 hours # Loading tasks tasks = [] for task in os.listdir(self.path): if os.path.isdir(os.path.join(self.path, task)): logger.info("Task %s found" % task) tasks.append(task) users = [] self.timedelta_params = ["token_min_interval", "token_gen_interval", "min_submission_interval", "min_user_test_interval", "per_user_time"] if os.path.isfile(os.path.join(self.path, "contest.yaml")): conf = yaml.safe_load(io.open(os.path.join(self.path, "contest.yaml"), "rt", encoding="utf-8")) logger.info("Loading YAML-encoded parameters for contest %s." % name) for key, param in conf.iteritems(): if key == "users": logger.info("User list found") self.users_conf = dict((user['username'], user) for user in param) users = self.users_conf.keys() elif key == "start" or key == "stop": args[key] = make_datetime(param) elif key in self.timedelta_params: args[key] = timedelta(seconds=param) elif key == "tasks": # To prevent task order logger.info("Task list found, overwrite existing") tasks = param elif key.startswith("token"): if key != "tokens_are_global": if conf.get("tokens_are_global", False): args[key] = param self.token_mode["token_mode"] = "infinite" else: args["token_mode"] = "infinite" self.token_mode[key] = param else: args[key] = param self.tasks_order = dict((name, num) for num, name in enumerate(tasks)) logger.info("Contest parameters loaded.") return Contest(**args), tasks, users
def post(self): fallback_page = "/tasks/add" try: attrs = dict() self.get_string(attrs, "name", empty=None) self.get_string(attrs, "category") assert attrs.get("name") is not None, "No task name specified." attrs["title"] = attrs["name"] # Set default submission format as ["taskname.%l"] attrs["submission_format"] = \ [SubmissionFormatElement("%s.%%l" % attrs["name"])] # Create the task. task = Task(**attrs) self.sql_session.add(task) except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return try: attrs = dict() # Create its first dataset. attrs["description"] = "Default" attrs["autojudge"] = True attrs["task_type"] = "Batch" attrs["task_type_parameters"] = '["alone", ["", ""], "diff"]' attrs["score_type"] = "Sum" attrs["score_type_parameters"] = '100' attrs["task"] = task dataset = Dataset(**attrs) self.sql_session.add(dataset) # Make the dataset active. Life works better that way. task.active_dataset = dataset except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Create the task on RWS. self.application.service.proxy_service.reinitialize() self.redirect("/task/%s" % task.id) else: self.redirect(fallback_page)
def get_contest(): contest = Mock() contest.id = get_int() contest.name = get_string() start = get_int(2 ** 11) duration = get_int(2 ** 8) contest.start = make_datetime(start) contest.stop = make_datetime(start + duration) contest.score_precision = 2 contest.description = get_string() return contest
def test_set_priority(self): """Test that priority get changed and item moved.""" self.queue.push(self.item_a, PriorityQueue.PRIORITY_LOW) self.queue.push(self.item_b, PriorityQueue.PRIORITY_MEDIUM, timestamp=make_datetime(10)) self.queue.push(self.item_c, PriorityQueue.PRIORITY_MEDIUM, timestamp=make_datetime(5)) self.queue.set_priority(self.item_a, PriorityQueue.PRIORITY_HIGH) self.assertTrue(self.queue._verify()) self.assertEqual(self.queue.top().item, self.item_a)
def post(self, dataset_id): fallback_page = "/dataset/%s/testcases/add" % dataset_id dataset = self.safe_get_item(Dataset, dataset_id) task = dataset.task codename = self.get_argument("codename") try: input_ = self.request.files["input"][0] output = self.request.files["output"][0] except KeyError: self.application.service.add_notification( make_datetime(), "Invalid data", "Please fill both input and output.") self.redirect(fallback_page) return public = self.get_argument("public", None) is not None task_name = task.name self.sql_session.close() try: input_digest = \ self.application.service.file_cacher.put_file_content( input_["body"], "Testcase input for task %s" % task_name) output_digest = \ self.application.service.file_cacher.put_file_content( output["body"], "Testcase output for task %s" % task_name) except Exception as error: self.application.service.add_notification( make_datetime(), "Testcase storage failed", repr(error)) self.redirect(fallback_page) return self.sql_session = Session() dataset = self.safe_get_item(Dataset, dataset_id) task = dataset.task testcase = Testcase( codename, public, input_digest, output_digest, dataset=dataset) self.sql_session.add(testcase) if self.try_commit(): # max_score and/or extra_headers might have changed. self.application.service.proxy_service.reinitialize() self.redirect("/task/%s" % task.id) else: self.redirect(fallback_page)
def post(self, contest_id): self.contest = self.safe_get_item(Contest, contest_id) subject = self.get_argument("subject", "") text = self.get_argument("text", "") if subject != "": ann = Announcement(make_datetime(), subject, text, contest=self.contest) self.sql_session.add(ann) self.try_commit() else: self.application.service.add_notification(make_datetime(), "Subject is mandatory.", "") self.redirect("/contest/%s/announcements" % contest_id)
def test_remove(self): """Test that items get removed.""" self.queue.push(self.item_a, PriorityQueue.PRIORITY_LOW) self.assertFalse(self.item_b in self.queue) self.queue.push(self.item_b, PriorityQueue.PRIORITY_MEDIUM, timestamp=make_datetime(10)) self.queue.push(self.item_c, PriorityQueue.PRIORITY_MEDIUM, timestamp=make_datetime(5)) self.assertTrue(self.item_b in self.queue) self.queue.remove(self.item_b) self.assertFalse(self.item_b in self.queue) self.queue._verify()
def post(self, contest_id): self.contest = self.safe_get_item(Contest, contest_id) subject = self.get_argument("subject", "") text = self.get_argument("text", "") if len(subject) > 0: ann = Announcement(make_datetime(), subject, text, contest=self.contest) self.sql_session.add(ann) self.try_commit() else: self.service.add_notification( make_datetime(), "Subject is mandatory.", "") self.redirect(self.url("contest", contest_id, "announcements"))
def post(self, task_id): fallback_page = self.url("task", task_id, "statements", "add") task = self.safe_get_item(Task, task_id) language = self.get_argument("language", "") if len(language) == 0: self.service.add_notification( make_datetime(), "No language code specified", "The language code can be any string.") self.redirect(fallback_page) return statement = self.request.files["statement"][0] if not statement["filename"].endswith(".pdf"): self.service.add_notification( make_datetime(), "Invalid task statement", "The task statement must be a .pdf file.") self.redirect(fallback_page) return task_name = task.name self.sql_session.close() try: digest = self.service.file_cacher.put_file_content( statement["body"], "Statement for task %s (lang: %s)" % (task_name, language)) except Exception as error: self.service.add_notification( make_datetime(), "Task statement storage failed", repr(error)) self.redirect(fallback_page) return # TODO verify that there's no other Statement with that language # otherwise we'd trigger an IntegrityError for constraint violation self.sql_session = Session() task = self.safe_get_item(Task, task_id) self.contest = task.contest statement = Statement(language, digest, task=task) self.sql_session.add(statement) if self.try_commit(): self.redirect(self.url("task", task_id)) else: self.redirect(fallback_page)
def post(self, task_id): fallback_page = "/task/%s/add_dataset" % task_id task = self.safe_get_item(Task, task_id) try: attrs = dict() self.get_string(attrs, "description") # Ensure description is unique. if any(attrs["description"] == d.description for d in task.datasets): self.application.service.add_notification( make_datetime(), "Dataset name %r is already taken." % attrs["description"], "Please choose a unique name for this dataset.") self.redirect(fallback_page) return self.get_time_limit(attrs, "time_limit") self.get_memory_limit(attrs, "memory_limit") self.get_task_type(attrs, "task_type", "TaskTypeOptions_") self.get_score_type(attrs, "score_type", "score_type_parameters") # Create the dataset. attrs["autojudge"] = False attrs["task"] = task dataset = Dataset(**attrs) self.sql_session.add(dataset) except Exception as error: logger.warning("Invalid field: %s" % (traceback.format_exc())) self.application.service.add_notification(make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return # If the task does not yet have an active dataset, make this # one active. if task.active_dataset is None: task.active_dataset = dataset if self.try_commit(): # self.application.service.scoring_service.reinitialize() self.redirect("/task/%s" % task_id) else: self.redirect(fallback_page)
def post(self, user_id): fallback_page = "/user/%s" % user_id user = self.safe_get_item(User, user_id) try: contest_id = self.get_argument("contest_id") assert contest_id != "null", "Please select a valid contest" except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return self.contest = self.safe_get_item(Contest, contest_id) attrs = {} self.get_bool(attrs, "hidden") # Create the participation. participation = Participation(contest=self.contest, user=user, hidden=attrs["hidden"]) self.sql_session.add(participation) if self.try_commit(): # Create the user on RWS. self.application.service.proxy_service.reinitialize() # Maybe they'll want to do this again (for another contest). self.redirect(fallback_page)
def render_params(self): """Return the default render params used by almost all handlers. return (dict): default render params """ params = {} params["rtd_version"] = "latest" if "dev" in __version__ \ else "v" + __version__[:3] params["timestamp"] = make_datetime() params["contest"] = self.contest params["url_root"] = get_url_root(self.request.path) if self.current_user is not None: params["current_user"] = self.current_user if self.contest is not None: params["phase"] = self.contest.phase(params["timestamp"]) # Keep "== None" in filter arguments. SQLAlchemy does not # understand "is None". params["unanswered"] = self.sql_session.query(Question)\ .join(Participation)\ .filter(Participation.contest_id == self.contest.id)\ .filter(Question.reply_timestamp == None)\ .filter(Question.ignored == False)\ .count() # noqa # TODO: not all pages require all these data. params["contest_list"] = self.sql_session.query(Contest).all() params["task_list"] = self.sql_session.query(Task).all() params["user_list"] = self.sql_session.query(User).all() params["team_list"] = self.sql_session.query(Team).all() return params
def post(self, admin_id): admin = self.safe_get_item(Admin, admin_id) try: new_attrs = _admin_attrs(self) except Exception as error: self.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(self.url("admin", admin_id)) return # If the admin is allowed here because has permission_all, # they can do anything they want, otherwise, if they are # allowed because they are editing their own details, they can # only change a subset of the fields. if not self.current_user.permission_all: for key in iterkeys(new_attrs): if key not in AdminHandler.SELF_MODIFIABLE_FIELDS: del new_attrs[key] admin.set_attrs(new_attrs) if self.try_commit(): logger.info("Admin %s updated.", admin.id) self.redirect(self.url("admins")) else: self.redirect(self.url("admin", admin_id))
def push(self, item, priority=None, timestamp=None): """Push an item in the queue. If timestamp is not specified, uses the current time. item (QueueItem): the item to add to the queue. priority (int|None): the priority of the item, or None for medium priority. timestamp (datetime|None): the time of the submission, or None to use now. return (bool): false if the element was already in the queue and was not pushed again, true otherwise.. """ if item in self._reverse: return False if priority is None: priority = PriorityQueue.PRIORITY_MEDIUM if timestamp is None: timestamp = make_datetime() index = self._next_index self._next_index += 1 self._queue.append(QueueEntry(item, priority, timestamp, index)) last = len(self._queue) - 1 self._reverse[item] = last self._up_heap(last) # Signal to listener greenlets that there might be something. self._event.set() return True
def post(self, task_id): fallback_page = self.url("task", task_id, "spoiler", "add") task = self.safe_get_item(Task, task_id) spoiler = self.request.files["spoiler"][0] task_name = task.name self.sql_session.close() try: digest = self.service.file_cacher.put_file_content( spoiler["body"], "Task spoiler for %s" % task_name) except Exception as error: self.service.add_notification( make_datetime(), "Spoiler storage failed", repr(error)) self.redirect(fallback_page) return # TODO verify that there's no other Spoiler with that filename # otherwise we'd trigger an IntegrityError for constraint violation self.sql_session = Session() task = self.safe_get_item(Task, task_id) spoiler = Spoiler(spoiler["filename"], digest, task=task) self.sql_session.add(spoiler) if self.try_commit(): self.redirect(self.url("task", task_id)) else: self.redirect(fallback_page)
def post(self, contest_id): contest = self.safe_get_item(Contest, contest_id) try: attrs = contest.get_attrs() self.get_string(attrs, "name", empty=None) self.get_string(attrs, "description") assert attrs.get("name") is not None, "No contest name specified." allowed_localizations = \ self.get_argument("allowed_localizations", "") if allowed_localizations: attrs["allowed_localizations"] = \ [x.strip() for x in allowed_localizations.split(",") if len(x) > 0 and not x.isspace()] else: attrs["allowed_localizations"] = [] attrs["languages"] = self.get_arguments("languages") self.get_bool(attrs, "submissions_download_allowed") self.get_bool(attrs, "block_hidden_participations") self.get_bool(attrs, "ip_restriction") self.get_bool(attrs, "ip_autologin") self.get_string(attrs, "token_mode") self.get_int(attrs, "token_max_number") self.get_timedelta_sec(attrs, "token_min_interval") self.get_int(attrs, "token_gen_initial") self.get_int(attrs, "token_gen_number") self.get_timedelta_min(attrs, "token_gen_interval") self.get_int(attrs, "token_gen_max") self.get_int(attrs, "max_submission_number") self.get_int(attrs, "max_user_test_number") self.get_timedelta_sec(attrs, "min_submission_interval") self.get_timedelta_sec(attrs, "min_user_test_interval") self.get_datetime(attrs, "start") self.get_datetime(attrs, "stop") self.get_string(attrs, "timezone", empty=None) self.get_timedelta_sec(attrs, "per_user_time") self.get_int(attrs, "score_precision") # Update the contest. contest.set_attrs(attrs) except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s).", repr(error)) self.redirect("/contest/%s" % contest_id) return if self.try_commit(): # Update the contest on RWS. self.application.service.proxy_service.reinitialize() self.redirect("/contest/%s" % contest_id)
def render_params(self): """Return the default render params used by almost all handlers. return (dict): default render params """ params = {} params["rtd_version"] = "latest" if "dev" in __version__ \ else "v" + __version__[:3] params["timestamp"] = make_datetime() params["contest"] = self.contest params["url"] = self.url params["xsrf_form_html"] = self.xsrf_form_html() # FIXME These objects provide too broad an access: their usage # should be extracted into with narrower-scoped parameters. params["config"] = config params["handler"] = self if self.current_user is not None: params["admin"] = self.current_user if self.contest is not None: params["phase"] = self.contest.phase(params["timestamp"]) params["unanswered"] = self.sql_session.query(Question)\ .join(Participation)\ .filter(Participation.contest_id == self.contest.id)\ .filter(Question.reply_timestamp.is_(None))\ .filter(Question.ignored.is_(False))\ .count() # TODO: not all pages require all these data. params["contest_list"] = self.sql_session.query(Contest).all() params["task_list"] = self.sql_session.query(Task).all() params["user_list"] = self.sql_session.query(User).all() params["team_list"] = self.sql_session.query(Team).all() return params
def post(self, contest_id): fallback_page = "/contest/%s/users" % contest_id self.contest = self.safe_get_item(Contest, contest_id) try: user_id = self.get_argument("user_id") assert user_id != "null", "Please select a valid user" except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return user = self.safe_get_item(User, user_id) # Create the participation. participation = Participation(contest=self.contest, user=user) self.sql_session.add(participation) if self.try_commit(): # Create the user on RWS. self.application.service.proxy_service.reinitialize() # Maybe they'll want to do this again (for another user) self.redirect(fallback_page)
def post(self): fallback_page = "/users/add" try: attrs = dict() self.get_string(attrs, "first_name") self.get_string(attrs, "last_name") self.get_string(attrs, "username", empty=None) self.get_string(attrs, "password") self.get_string(attrs, "email") assert attrs.get("username") is not None, \ "No username specified." self.get_string(attrs, "timezone", empty=None) self.get_string(attrs, "preferred_languages") # Create the user. user = User(**attrs) self.sql_session.add(user) except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Create the user on RWS. self.application.service.proxy_service.reinitialize() self.redirect("/user/%s" % user.id) else: self.redirect(fallback_page)
def format_datetime_smart(dt, timezone, locale=None): """Return dt formatted as '[date] time'. Date is present in the output if it is not today. dt (datetime): a datetime object. timezone (tzinfo): the timezone the output should be in. return (str): the [date and] time of dt, formatted using the given locale. """ if locale is None: locale = tornado.locale.get() _ = locale.translate # convert dt and 'now' from UTC to local time dt = dt.replace(tzinfo=utc).astimezone(timezone) now = make_datetime().replace(tzinfo=utc).astimezone(timezone) if dt.date() == now.date(): return dt.strftime(_("%H:%M:%S")) else: return dt.strftime(_("%Y-%m-%d %H:%M:%S"))
def post(self, team_id): fallback_page = "/team/%s" % team_id team = self.safe_get_item(Team, team_id) try: attrs = team.get_attrs() self.get_string(attrs, "code") self.get_string(attrs, "name") assert attrs.get("code") is not None, \ "No team code specified." # Update the team. team.set_attrs(attrs) except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Update the team on RWS. self.application.service.proxy_service.reinitialize() self.redirect(fallback_page)
def post(self): fallback_page = "/teams/add" try: attrs = dict() self.get_string(attrs, "code") self.get_string(attrs, "name") assert attrs.get("code") is not None, \ "No team code specified." # Create the team. team = Team(**attrs) self.sql_session.add(team) except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Create the team on RWS. self.application.service.proxy_service.reinitialize() # In case other teams need to be added. self.redirect(fallback_page)
def post(self, user_id): fallback_page = "/user/%s" % user_id user = self.safe_get_item(User, user_id) try: attrs = user.get_attrs() self.get_string(attrs, "first_name") self.get_string(attrs, "last_name") self.get_string(attrs, "username", empty=None) self.get_string(attrs, "password") self.get_string(attrs, "email") self.get_string(attrs, "preferred_languages") self.get_string(attrs, "timezone", empty=None) assert attrs.get("username") is not None, \ "No username specified." # Update the user. user.set_attrs(attrs) except Exception as error: self.application.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.application.service.proxy_service.reinitialize() self.redirect(fallback_page)
def post(self, contest_id): fallback_page = "/contest/%s/users" % contest_id self.contest = self.safe_get_item(Contest, contest_id) try: user_id = self.get_argument("user_id") operation = self.get_argument("operation") assert operation in ( self.REMOVE_FROM_CONTEST, ), "Please select a valid operation" except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return user = self.safe_get_item(User, user_id) if operation == self.REMOVE_FROM_CONTEST: # Unassign the user from the contest. participation = self.sql_session.query(Participation)\ .filter(Participation.user == user)\ .filter(Participation.contest == self.contest)\ .first() self.sql_session.delete(participation) if self.try_commit(): # Create the user on RWS. self.application.service.proxy_service.reinitialize() # Maybe they'll want to do this again (for another task) self.redirect(fallback_page)
def get(self): res = [] last_notification = make_datetime( float(self.get_argument("last_notification", "0"))) # Keep "== None" in filter arguments. SQLAlchemy does not # understand "is None". questions = self.sql_session.query(Question)\ .filter(Question.reply_timestamp == None)\ .filter(Question.question_timestamp > last_notification)\ .all() # noqa for question in questions: res.append({ "type": "new_question", "timestamp": make_timestamp(question.question_timestamp), "subject": question.subject, "text": question.text, "contest_id": question.participation.contest_id }) # Simple notifications for notification in self.application.service.notifications: res.append({"type": "notification", "timestamp": make_timestamp(notification[0]), "subject": notification[1], "text": notification[2]}) self.application.service.notifications = [] self.write(json.dumps(res))
def post(self): fallback_page = "/contests/add" try: attrs = dict() self.get_string(attrs, "name", empty=None) assert attrs.get("name") is not None, "No contest name specified." attrs["description"] = attrs["name"] # Create the contest. contest = Contest(**attrs) self.sql_session.add(contest) except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Create the contest on RWS. self.application.service.proxy_service.reinitialize() self.redirect("/contest/%s" % contest.id) else: self.redirect(fallback_page)
def post(self, user_id): fallback_page = "/user/%s" % user_id user = self.safe_get_item(User, user_id) try: contest_id = self.get_argument("contest_id") operation = self.get_argument("operation") assert contest_id != "null", "Please select a valid contest" assert operation in ( "Remove", ), "Please select a valid operation" except Exception as error: self.application.service.add_notification( make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return self.contest = self.safe_get_item(Contest, contest_id) if operation == "Remove": # Remove the participation. participation = self.sql_session.query(Participation)\ .filter(Participation.user == user)\ .filter(Participation.contest == self.contest)\ .first() self.sql_session.delete(participation) if self.try_commit(): # Create the user on RWS. self.application.service.proxy_service.reinitialize() # Maybe they'll want to do this again (for another contest). self.redirect(fallback_page)
def post(self, dataset_id): fallback_page = "/dataset/%s/managers/add" % dataset_id dataset = self.safe_get_item(Dataset, dataset_id) task = dataset.task manager = self.request.files["manager"][0] task_name = task.name self.sql_session.close() try: digest = self.application.service.file_cacher.put_file_content( manager["body"], "Task manager for %s" % task_name) except Exception as error: self.application.service.add_notification( make_datetime(), "Manager storage failed", repr(error)) self.redirect(fallback_page) return self.sql_session = Session() dataset = self.safe_get_item(Dataset, dataset_id) task = dataset.task manager = Manager(manager["filename"], digest, dataset=dataset) self.sql_session.add(manager) if self.try_commit(): self.redirect("/task/%s" % task.id) else: self.redirect(fallback_page)
def _get_current_user_from_cookie(self): """Return the current participation based on the cookie. If a participation can be extracted, the cookie is refreshed. return (Participation|None): the participation extracted from the cookie, or None if not possible. """ cookie_name = self.contest.name + "_login" if self.get_secure_cookie(cookie_name) is None: return None # Parse cookie. try: cookie = pickle.loads(self.get_secure_cookie(cookie_name)) username = cookie[0] password = cookie[1] last_update = make_datetime(cookie[2]) except: return None # Check if the cookie is expired. if self.timestamp - last_update > \ timedelta(seconds=config.cookie_duration): return None # Load participation from DB and make sure it exists. participation = self.sql_session.query(Participation)\ .join(Participation.user)\ .options(contains_eager(Participation.user))\ .filter(Participation.contest == self.contest)\ .filter(User.username == username)\ .first() if participation is None: return None # Check that the password is correct (if a contest-specific # password is defined, use that instead of the user password). if participation.password is None: correct_password = participation.user.password else: correct_password = participation.password if password != correct_password: return None if self.refresh_cookie: self.set_secure_cookie(cookie_name, pickle.dumps( (username, password, make_timestamp())), expires_days=None) return participation
def setUp(self): super(TestAuthenticateRequest, self).setUp() self.timestamp = make_datetime() self.contest = self.add_contest() self.user = self.add_user( username="******", password=build_password("mypass")) self.participation = self.add_participation( contest=self.contest, user=self.user) _, self.cookie = validate_login( self.session, self.contest, self.timestamp, self.user.username, "mypass", ipaddress.ip_address("10.0.0.1"))
def import_object(self, data): """Import objects from the given data (without relationships). The given data is assumed to be a dict in the format produced by ContestExporter. This method reads the "_class" item and tries to find the corresponding class. Then it loads all column properties of that class (those that are present in the data) and uses them as keyword arguments in a call to the class constructor (if a required property is missing this call will raise an error). Relationships are not handled by this method, since we may not have all referenced objects available yet. Thus we prefer to add relationships in a later moment, using the add_relationships method. Note that both this method and add_relationships don't check if the given data has more items than the ones we understand and use. """ cls = getattr(class_hook, data["_class"]) args = dict() for prp in cls._col_props: if prp.key not in data: # We will let the __init__ of the class check if any # argument is missing, so it's safe to just skip here. continue col = prp.columns[0] col_type = type(col.type) val = data[prp.key] if col_type in \ [Boolean, Integer, Float, Unicode, RepeatedUnicode, Enum]: args[prp.key] = val elif col_type is String: args[prp.key] = \ val.encode('latin1') if val is not None else None elif col_type is DateTime: args[prp.key] = \ make_datetime(val) if val is not None else None elif col_type is Interval: args[prp.key] = \ timedelta(seconds=val) if val is not None else None else: raise RuntimeError( "Unknown SQLAlchemy column type: %s" % col_type) return cls(**args)
def try_commit(self): """Try to commit the current session. If not successful display a warning in the webpage. return (bool): True if commit was successful, False otherwise. """ try: self.sql_session.commit() except IntegrityError as error: self.application.service.add_notification(make_datetime(), "Operation failed.", "%s" % error) return False else: self.application.service.add_notification(make_datetime(), "Operation successful.", "") return True
def post(self): task_id = self.get_argument("task_id") operation = self.get_argument("operation") if operation == self.REMOVE: asking_page = "/tasks/%s/remove" % task_id # Open asking for remove page self.redirect(asking_page) else: self.application.service.add_notification( make_datetime(), "Invalid operation %s" % operation, "") self.redirect("/tasks")
def post(self): user_id = self.get_argument("user_id") operation = self.get_argument("operation") if operation == self.REMOVE: asking_page = self.url("users", user_id, "remove") # Open asking for remove page self.redirect(asking_page) else: self.service.add_notification( make_datetime(), "Invalid operation %s" % operation, "") self.redirect(self.url("contests"))
def post(self, dataset_id): dataset = self.safe_get_item(Dataset, dataset_id) task = dataset.task task.active_dataset = dataset if self.try_commit(): self.application.service.proxy_service.dataset_updated( task_id=task.id) # This kicks off judging of any submissions which were previously # unloved, but are now part of an autojudged taskset. self.application.service\ .evaluation_service.search_operations_not_done() self.application.service\ .scoring_service.search_operations_not_done() # Now send notifications to contestants. datetime = make_datetime() r = re.compile('notify_([0-9]+)$') count = 0 for k in self.request.arguments: m = r.match(k) if not m: continue participation = self.safe_get_item(Participation, m.group(1)) message = Message(datetime, self.get_argument("message_subject", ""), self.get_argument("message_text", ""), participation=participation) self.sql_session.add(message) count += 1 if self.try_commit(): self.application.service.add_notification( make_datetime(), "Messages sent to %d users." % count, "") self.redirect("/task/%s" % task.id)
def make_utc_datetime(t): """Consider the case of time specified in string """ if isinstance(t, str): # Parse and validate a datetime (in pseudo-ISO8601). if '.' not in t: t += ".0" try: return datetime.strptime(t, "%Y-%m-%d %H:%M:%S.%f") except: raise ValueError("Can't cast %s to datetime." % t) else: return make_datetime(t)
def setUp(self): super().setUp() self.contest = self.add_contest() self.participation = self.add_participation(contest=self.contest) self.task = self.add_task(contest=self.contest) self.submission = self.add_submission(participation=self.participation, task=self.task) patcher = patch("cms.server.contest.tokening.tokens_available") self.tokens_available = patcher.start() self.addCleanup(patcher.stop) self.timestamp = make_datetime()
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 today(ctx, dt): """Returns whether the given datetime is today. ctx (Context): a Jinja2 context, needed to retrieve the current datetime and the timezone to use when comparing. dt (datetime): a datetime. return (bool): whether dt occurred today in the timezone. """ now = ctx.get("now", make_datetime()) timezone = ctx.get("timezone", local_tz) return dt.replace(tzinfo=utc).astimezone(timezone).date() \ == now.replace(tzinfo=utc).astimezone(timezone).date()
def get_contest_object(self): """ Return the Contest database object. """ args = {} # Names. args["name"] = self.params["short_name"] args["description"] = self.params["long_name"] # Languages. args["languages"] = self.params["languages"] # Communication args["allow_questions"] = self.params.get("allow_questions", False) # Times. start_time = time_from_str(self.params["start_time"]) stop_time = time_from_str(self.params["end_time"]) args["start"] = make_datetime(time.mktime(start_time.timetuple())) args["stop"] = make_datetime(time.mktime(stop_time.timetuple())) # Limits. args["max_submission_number"] = self.params["max_submission_number"] args["max_user_test_number"] = self.params["max_user_test_number"] interval_seconds = self.params["min_submission_interval"] if interval_seconds is not None: delta = timedelta(seconds=interval_seconds) args["min_submission_interval"] = delta interval_seconds = self.params["min_user_test_interval"] if interval_seconds is not None: delta = timedelta(seconds=interval_seconds) args["min_user_test_interval"] = delta return Contest(**args)
def assertSubmissionInDb(self, timestamp, task, language, files): """Assert that the submission with the given data is in the DB.""" db_submissions = self.session.query(Submission)\ .filter(Submission.timestamp == make_datetime(timestamp))\ .filter(Submission.task_id == task.id).all() self.assertEqual(len(db_submissions), 1) s = db_submissions[0] self.assertEqual(s.participation_id, self.participation.id) self.assertEqual(s.language, language) # Check the submission's files are exactly those expected. db_files = self.session.query(File)\ .filter(File.submission_id == s.id).all() db_files_dict = dict((f.filename, f.digest) for f in db_files) self.assertEqual(files, db_files_dict)
def prepare(self): """This method is executed at the beginning of each request. """ self.timestamp = make_datetime() self.set_header("Cache-Control", "no-cache, must-revalidate") self.sql_session = Session() self.contest = Contest.get_from_id(self.application.service.contest, self.sql_session) self._ = self.locale.translate self.r_params = self.render_params()
def setUp(self): super(TestCheckMinInterval, self).setUp() patcher = \ patch("cms.server.contest.submission.check.get_latest_submission") self.get_latest_submission = patcher.start() self.addCleanup(patcher.stop) self.calls = list() self.contest = self.add_contest() self.task = self.add_task(contest=self.contest) self.participation = self.add_participation(unrestricted=False, contest=self.contest) self.timestamp = make_datetime()
def get_submission(task=None, participation=None, sr=None, scored=True): task = task if task is not None else get_task() participation = participation if participation is not None \ else get_participation() sr = sr if sr is not None else get_sr(scored=scored) submission = Mock() submission.timestamp = make_datetime(get_int(2 ** 11 + 2 ** 8, 2 ** 11)) submission.tokened.return_value = False submission.get_result.return_value = sr submission.participation = participation submission.task = task submission.id = get_int() return submission
def post(self, dataset_id): fallback_page = \ self.url("dataset", dataset_id, "testcases", "download") dataset = self.safe_get_item(Dataset, dataset_id) # Get zip file name, input/output file names templates, # or use default ones. zip_filename = self.get_argument("zip_filename", "testcases.zip") input_template = self.get_argument("input_template", "input.*") output_template = self.get_argument("output_template", "output.*") # Template validations if input_template.count('*') != 1 or output_template.count('*') != 1: self.service.add_notification( make_datetime(), "Invalid template format", "You must have exactly one '*' in input/output template.") self.redirect(fallback_page) return # Replace input/output template placeholder with the python format. input_template = input_template.strip().replace("*", "%s") output_template = output_template.strip().replace("*", "%s") # FIXME When Tornado will stop having the WSGI adapter buffer # the whole response, we could use a tempfile.TemporaryFile so # to avoid having the whole ZIP file in memory at once. temp_file = io.BytesIO() with zipfile.ZipFile(temp_file, "w") as zip_file: for testcase in itervalues(dataset.testcases): # Get input, output file path input_path = self.service.file_cacher.\ get_file(testcase.input).name output_path = self.service.file_cacher.\ get_file(testcase.output).name zip_file.write( input_path, input_template % testcase.codename) zip_file.write( output_path, output_template % testcase.codename) self.set_header("Content-Type", "application/zip") self.set_header("Content-Disposition", "attachment; filename=\"%s\"" % zip_filename) self.write(temp_file.getvalue())
def delete(self, group_id): group = self.safe_get_item(User, group_id) fallback_page = self.url("contest", group.contest_id, "groups") if len(group.participations) != 0: self.application.service.add_notification( make_datetime(), "Cannot delete group because it contains users.") self.redirect(fallback_page) return self.sql_session.delete(group) self.try_commit() # Maybe they'll want to do this again (for another user) self.write("../../groups")
def check_timeouts(self): """Check if some worker is not responding in too much time. If this is the case, the worker is scheduled for disabling, and we send him a message trying to shut it down. return ([ESOperation]): list of operations assigned to worker that timeout. """ now = make_datetime() lost_operations = [] for shard in self._worker: if self._start_time[shard] is not None: active_for = now - self._start_time[shard] if active_for > WorkerPool.WORKER_TIMEOUT: # Here shard is a working worker with no sign of # intelligent life for too much time. logger.error( "Disabling and shutting down " "worker %d because of no response " "in %s.", shard, active_for) is_busy = ( self._operations[shard] != WorkerPool.WORKER_INACTIVE and self._operations[shard] != WorkerPool.WORKER_DISABLED) assert is_busy # We return the operation so ES can do what it needs. if not self._ignore[shard] and \ isinstance(self._operations[shard], list): for operation in self._operations[shard]: if operation not in \ self._operations_to_ignore[shard]: lost_operations.append(operation) # Also, we are not trusting it, so we are not # assigning him new operations even if it comes back to # life. self._schedule_disabling[shard] = True self._ignore[shard] = True self.release_worker(shard) self._worker[shard].quit(reason="No response in %s." % active_for) return lost_operations
def post(self, contest_id, user_id): fallback_page = "/contest/%s/user/%s" % (contest_id, user_id) 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_string(attrs, "password", empty=None) self.get_ip_address_or_subnet(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, "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 except Exception as error: self.application.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.application.service.proxy_service.reinitialize() self.redirect(fallback_page)
def acquire_worker(self, operation, side_data=None): """Tries to assign an operation to an available worker. If no workers are available then this returns None, otherwise this returns the chosen worker. operation (ESOperation): the operation to assign to a worker. side_data (object): object to attach to the worker for later use. return (int|None): None if no workers are available, the worker assigned to the operation otherwise. """ # We look for an available worker. try: shard = self.find_worker(WorkerPool.WORKER_INACTIVE, require_connection=True, random_worker=True) except LookupError: self._workers_available_event.clear() return None # Then we fill the info for future memory. self._operation[shard] = operation self._start_time[shard] = make_datetime() self._side_data[shard] = side_data logger.debug("Worker %s acquired.", shard) # And finally we ask the worker to do the operation. timestamp = side_data[1] queue_time = self._start_time[shard] - timestamp logger.info("Asking worker %s to `%s' (%s after submission).", shard, operation, queue_time) with SessionGen() as session: job = operation.build_job(session) job_dict = job.export_to_dict() self._worker[shard].execute_job( job_dict=job_dict, callback=self._service.action_finished, plus=(operation.type_, operation.object_id, operation.dataset_id, operation.testcase_codename, side_data, shard)) return shard
def post(self, dataset_id): fallback_page = \ self.url("dataset", dataset_id, "testcases", "download") dataset = self.safe_get_item(Dataset, dataset_id) # Get zip file name, input/output file names templates, # or use default ones. zip_filename = self.get_argument("zip_filename", "testcases.zip") input_template = self.get_argument("input_template", "input.*") output_template = self.get_argument("output_template", "output.*") # Template validations if input_template.count('*') != 1 or output_template.count('*') != 1: self.application.service.add_notification( make_datetime(), "Invalid template format", "You must have exactly one '*' in input/output template.") self.redirect(fallback_page) return # Replace input/output template placeholder with the python format. input_template = input_template.strip().replace("*", "%s") output_template = output_template.strip().replace("*", "%s") # Create a temp dir to contain the content of the zip file. tempdir = tempfile.mkdtemp(dir=config.temp_dir) zip_path = os.path.join(tempdir, "testcases.zip") with zipfile.ZipFile(zip_path, "w") as zip_file: for testcase in dataset.testcases.itervalues(): # Get input, output file path input_path = self.application.service.file_cacher.\ get_file(testcase.input).name output_path = self.application.service.file_cacher.\ get_file(testcase.output).name zip_file.write( input_path, input_template % testcase.codename) zip_file.write( output_path, output_template % testcase.codename) zip_file.close() self.fetch_from_filesystem(zip_path, "application/zip", zip_filename) shutil.rmtree(tempdir)
def post(self, submission_id, dataset_id=None): submission = self.safe_get_item(Submission, submission_id) try: attrs = {"comment": submission.comment} self.get_string(attrs, "comment") submission.set_attrs(attrs) except Exception as error: self.service.add_notification(make_datetime(), "Invalid field(s)", repr(error)) else: self.try_commit() if dataset_id is None: self.redirect(self.url("submission", submission_id)) else: self.redirect(self.url("submission", submission_id, dataset_id))
def post(self): fallback_page = self.url("users", "add") try: attrs = dict() self.get_string(attrs, "first_name") self.get_string(attrs, "last_name") self.get_string(attrs, "username", empty=None) self.get_int(attrs, "grade") self.get_string(attrs, "city_region") self.get_string(attrs, "school_name") self.get_password(attrs, None, False) self.get_string(attrs, "email", empty=None) assert attrs.get("username") is not None, \ "No username specified." self.get_string(attrs, "timezone", empty=None) self.get_string_list(attrs, "preferred_languages") # Create the user. user = User(**attrs) self.sql_session.add(user) except Exception as error: self.service.add_notification(make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): # Create the user on RWS. self.service.proxy_service.reinitialize() self.redirect(self.url("user", user.id)) else: self.redirect(fallback_page)
def post(self): fallback_page = self.url("admins", "add") try: attrs = _admin_attrs(self) assert attrs.get("authentication") is not None, \ "Empty password not permitted." admin = Admin(**attrs) self.sql_session.add(admin) except Exception as error: self.service.add_notification(make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if self.try_commit(): self.redirect(self.url("admins")) else: self.redirect(fallback_page)
def format_datetime_smart(dt, timezone, locale=None): """Return dt formatted as 'date & time' or, if date is today, just 'time' dt (datetime): a datetime object timezone (tzinfo): the timezone the output should be in return (str): the [date and] time of dt, formatted using the given locale """ if locale is None: locale = tornado.locale.get() _ = locale.translate # convert dt and 'now' from UTC to local time dt = dt.replace(tzinfo=utc).astimezone(timezone) now = make_datetime().replace(tzinfo=utc).astimezone(timezone) if dt.date() == now.date(): return dt.strftime(_("%H:%M:%S")) else: return dt.strftime(_("%Y-%m-%d %H:%M:%S"))
def render_params(self): """Return the default render params used by almost all handlers. return (dict): default render params """ params = {} params["rtd_version"] = "latest" if "dev" in __version__ \ else "v" + __version__[:3] params["timestamp"] = make_datetime() params["contest"] = self.contest params["url"] = self.url params["xsrf_form_html"] = self.xsrf_form_html() # FIXME These objects provide too broad an access: their usage # should be extracted into with narrower-scoped parameters. params["config"] = config params["handler"] = self if self.current_user is not None: params["admin"] = self.current_user if self.contest is not None: params["phase"] = self.contest.phase(params["timestamp"]) params["unanswered"] = self.sql_session.query(Question)\ .filter(~Question.subject.contains('#water'))\ .filter(~Question.subject.contains('#banana'))\ .filter(~Question.subject.contains('#apple'))\ .filter(~Question.subject.contains('#chocolate'))\ .filter(~Question.subject.contains('#cupcake'))\ .filter(~Question.subject.contains('#paper'))\ .filter(~Question.subject.contains('#wc'))\ .join(Participation)\ .filter(Participation.contest_id == self.contest.id)\ .filter(Question.reply_timestamp.is_(None))\ .filter(Question.ignored.is_(False))\ .count() # TODO: not all pages require all these data. params["contest_list"] = self.sql_session.query(Contest).all() params["task_list"] = self.sql_session.query(Task).all() params["user_list"] = self.sql_session.query(User).all() params["team_list"] = self.sql_session.query(Team).all() return params
def get(self): res = [] last_notification = make_datetime( float(self.get_argument("last_notification", "0"))) # Keep "== None" in filter arguments. SQLAlchemy does not # understand "is None". questions = self.sql_session.query(Question)\ .filter(~Question.subject.contains('#water'))\ .filter(~Question.subject.contains('#banana'))\ .filter(~Question.subject.contains('#apple'))\ .filter(~Question.subject.contains('#chocolate'))\ .filter(~Question.subject.contains('#cupcake'))\ .filter(~Question.subject.contains('#paper'))\ .filter(~Question.subject.contains('#wc'))\ .filter(Question.reply_timestamp.is_(None))\ .filter(Question.question_timestamp > last_notification)\ .all() for question in questions: res.append({ "type": "new_question", "timestamp": make_timestamp(question.question_timestamp), "subject": question.subject, "text": question.text, "contest_id": question.participation.contest_id }) # Simple notifications for notification in self.service.notifications: res.append({ "type": "notification", "timestamp": make_timestamp(notification[0]), "subject": notification[1], "text": notification[2] }) self.service.notifications = [] self.write(json.dumps(res))
def post(self, contest_id): fallback_page = self.url("contest", contest_id, "users") try: user_id = self.get_argument("user_id") operation = self.get_argument("operation") assert operation in ( self.REMOVE_FROM_CONTEST, ), "Please select a valid operation" except Exception as error: self.service.add_notification(make_datetime(), "Invalid field(s)", repr(error)) self.redirect(fallback_page) return if operation == self.REMOVE_FROM_CONTEST: asking_page = \ self.url("contest", contest_id, "user", user_id, "remove") # Open asking for remove page self.redirect(asking_page) return self.redirect(fallback_page)
def post(self, contest_id, user_id): user = self.safe_get_item(User, user_id) self.contest = self.safe_get_item(Contest, contest_id) participation = self.sql_session.query(Participation)\ .filter(Participation.contest == self.contest)\ .filter(Participation.user == user)\ .first() # check that the participation is valid if participation is None: raise tornado.web.HTTPError(404) message = Message(make_datetime(), self.get_argument("message_subject", ""), self.get_argument("message_text", ""), participation=participation) self.sql_session.add(message) if self.try_commit(): logger.info("Message submitted to user %s in contest %s.", user.username, self.contest.name) self.redirect("/contest/%s/user/%s" % (self.contest.id, user.id))