2020-01-21 06:35:58 +00:00
|
|
|
from abc import ABCMeta, abstractmethod, abstractproperty
|
2022-11-18 22:59:58 +00:00
|
|
|
from django.db.models import Max
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
class abstractclassmethod(classmethod):
|
|
|
|
__isabstractmethod__ = True
|
|
|
|
|
|
|
|
def __init__(self, callable):
|
|
|
|
callable.__isabstractmethod__ = True
|
|
|
|
super(abstractclassmethod, self).__init__(callable)
|
|
|
|
|
|
|
|
|
2022-11-01 01:43:06 +00:00
|
|
|
class BaseContestFormat(metaclass=ABCMeta):
|
2020-01-21 06:35:58 +00:00
|
|
|
@abstractmethod
|
|
|
|
def __init__(self, contest, config):
|
|
|
|
self.config = config
|
|
|
|
self.contest = contest
|
|
|
|
|
2023-01-02 23:22:45 +00:00
|
|
|
# Use in ioi16 to display ranking with hidden subtasks
|
|
|
|
self.has_hidden_subtasks = False
|
|
|
|
|
2020-01-21 06:35:58 +00:00
|
|
|
@abstractproperty
|
|
|
|
def name(self):
|
|
|
|
"""
|
|
|
|
Name of this contest format. Should be invoked with gettext_lazy.
|
|
|
|
|
|
|
|
:return: str
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
@abstractclassmethod
|
|
|
|
def validate(cls, config):
|
|
|
|
"""
|
|
|
|
Validates the contest format configuration.
|
|
|
|
|
|
|
|
:param config: A dictionary containing the configuration for this contest format.
|
|
|
|
:return: None
|
|
|
|
:raises: ValidationError
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def update_participation(self, participation):
|
|
|
|
"""
|
|
|
|
Updates a ContestParticipation object's score, cumtime, and format_data fields based on this contest format.
|
|
|
|
Implementations should call ContestParticipation.save().
|
|
|
|
|
|
|
|
:param participation: A ContestParticipation object.
|
|
|
|
:return: None
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
@abstractmethod
|
2022-12-28 20:38:32 +00:00
|
|
|
def display_user_problem(self, participation, contest_problem, show_final):
|
2020-01-21 06:35:58 +00:00
|
|
|
"""
|
|
|
|
Returns the HTML fragment to show a user's performance on an individual problem. This is expected to use
|
|
|
|
information from the format_data field instead of computing it from scratch.
|
|
|
|
|
|
|
|
:param participation: The ContestParticipation object linking the user to the contest.
|
|
|
|
:param contest_problem: The ContestProblem object representing the problem in question.
|
|
|
|
:return: An HTML fragment, marked as safe for Jinja2.
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
@abstractmethod
|
2022-12-28 20:38:32 +00:00
|
|
|
def display_participation_result(self, participation, show_final):
|
2020-01-21 06:35:58 +00:00
|
|
|
"""
|
|
|
|
Returns the HTML fragment to show a user's performance on the whole contest. This is expected to use
|
|
|
|
information from the format_data field instead of computing it from scratch.
|
|
|
|
|
|
|
|
:param participation: The ContestParticipation object.
|
|
|
|
:return: An HTML fragment, marked as safe for Jinja2.
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get_problem_breakdown(self, participation, contest_problems):
|
|
|
|
"""
|
|
|
|
Returns a machine-readable breakdown for the user's performance on every problem.
|
|
|
|
|
|
|
|
:param participation: The ContestParticipation object.
|
|
|
|
:param contest_problems: The list of ContestProblem objects to display performance for.
|
|
|
|
:return: A list of dictionaries, whose content is to be determined by the contest system.
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
2021-05-24 20:00:36 +00:00
|
|
|
@abstractmethod
|
|
|
|
def get_contest_problem_label_script(self):
|
|
|
|
"""
|
|
|
|
Returns the default Lua script to generate contest problem labels.
|
|
|
|
:return: A string, the Lua script.
|
|
|
|
"""
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
2020-01-21 06:35:58 +00:00
|
|
|
@classmethod
|
|
|
|
def best_solution_state(cls, points, total):
|
|
|
|
if not points:
|
2022-05-14 17:57:27 +00:00
|
|
|
return "failed-score"
|
2020-01-21 06:35:58 +00:00
|
|
|
if points == total:
|
2022-05-14 17:57:27 +00:00
|
|
|
return "full-score"
|
|
|
|
return "partial-score"
|
2022-11-18 22:59:58 +00:00
|
|
|
|
|
|
|
def handle_frozen_state(self, participation, format_data):
|
2023-01-02 23:22:45 +00:00
|
|
|
hidden_subtasks = {}
|
|
|
|
if hasattr(self, "get_hidden_subtasks"):
|
|
|
|
hidden_subtasks = self.get_hidden_subtasks()
|
2022-12-20 08:24:24 +00:00
|
|
|
|
2022-11-18 22:59:58 +00:00
|
|
|
queryset = participation.submissions.values("problem_id").annotate(
|
|
|
|
time=Max("submission__date")
|
|
|
|
)
|
|
|
|
for result in queryset:
|
|
|
|
problem = str(result["problem_id"])
|
|
|
|
if format_data.get(problem):
|
2022-12-20 08:24:24 +00:00
|
|
|
is_after_freeze = (
|
|
|
|
self.contest.freeze_after
|
|
|
|
and result["time"]
|
|
|
|
>= self.contest.freeze_after + participation.start
|
|
|
|
)
|
2023-01-02 23:22:45 +00:00
|
|
|
if is_after_freeze or hidden_subtasks.get(problem):
|
2022-11-18 22:59:58 +00:00
|
|
|
format_data[problem]["frozen"] = True
|
|
|
|
else:
|
|
|
|
format_data[problem] = {"time": 0, "points": 0, "frozen": True}
|