NDOJ/judge/contest_format/base.py

113 lines
3.9 KiB
Python
Raw Normal View History

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
@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
def display_user_problem(self, participation, contest_problem):
"""
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
def display_participation_result(self, participation):
"""
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):
if not self.contest.freeze_after:
return
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):
if result["time"] >= self.contest.freeze_after + participation.start:
format_data[problem]["frozen"] = True
else:
format_data[problem] = {"time": 0, "points": 0, "frozen": True}