NDOJ/judge/models/problem_data.py

251 lines
8.2 KiB
Python
Raw Normal View History

2020-01-21 06:35:58 +00:00
import errno
import os
2021-06-01 20:34:42 +00:00
from zipfile import BadZipFile, ZipFile
2020-01-21 06:35:58 +00:00
2020-01-24 02:32:46 +00:00
from django.core.validators import FileExtensionValidator
2021-06-01 20:34:42 +00:00
from django.core.cache import cache
2020-01-21 06:35:58 +00:00
from django.db import models
from django.utils.translation import gettext_lazy as _
2021-06-01 20:34:42 +00:00
from judge.utils.problem_data import ProblemDataStorage, get_file_cachekey
2020-01-21 06:35:58 +00:00
2022-05-14 17:57:27 +00:00
__all__ = [
"problem_data_storage",
"problem_directory_file",
"ProblemData",
"ProblemTestCase",
"CHECKERS",
]
2020-01-21 06:35:58 +00:00
problem_data_storage = ProblemDataStorage()
2022-08-31 03:50:08 +00:00
def problem_directory_file_helper(code, filename):
2020-01-21 06:35:58 +00:00
return os.path.join(code, os.path.basename(filename))
def problem_directory_file(data, filename):
2022-08-31 03:50:08 +00:00
return problem_directory_file_helper(data.problem.code, filename)
2020-01-21 06:35:58 +00:00
CHECKERS = (
2022-05-14 17:57:27 +00:00
("standard", _("Standard")),
("floats", _("Floats")),
("floatsabs", _("Floats (absolute)")),
("floatsrel", _("Floats (relative)")),
("rstripped", _("Non-trailing spaces")),
("sorted", _("Unordered")),
("identical", _("Byte identical")),
("linecount", _("Line-by-line")),
("custom", _("Custom checker (PY)")),
2024-03-19 04:53:35 +00:00
("customcpp", _("Custom checker (CPP)")),
2022-05-14 17:57:27 +00:00
("interact", _("Interactive")),
2022-06-22 07:28:34 +00:00
("testlib", _("Testlib")),
2020-01-21 06:35:58 +00:00
)
class ProblemData(models.Model):
2022-05-14 17:57:27 +00:00
problem = models.OneToOneField(
"Problem",
verbose_name=_("problem"),
related_name="data_files",
on_delete=models.CASCADE,
)
zipfile = models.FileField(
verbose_name=_("data zip file"),
storage=problem_data_storage,
null=True,
blank=True,
upload_to=problem_directory_file,
)
generator = models.FileField(
verbose_name=_("generator file"),
storage=problem_data_storage,
null=True,
blank=True,
upload_to=problem_directory_file,
)
output_prefix = models.IntegerField(
verbose_name=_("output prefix length"), blank=True, null=True
)
output_limit = models.IntegerField(
verbose_name=_("output limit length"), blank=True, null=True
)
feedback = models.TextField(
verbose_name=_("init.yml generation feedback"), blank=True
)
checker = models.CharField(
max_length=10, verbose_name=_("checker"), choices=CHECKERS, blank=True
)
checker_args = models.TextField(
verbose_name=_("checker arguments"),
blank=True,
help_text=_("checker arguments as a JSON object"),
)
custom_checker = models.FileField(
verbose_name=_("custom checker file"),
storage=problem_data_storage,
null=True,
blank=True,
upload_to=problem_directory_file,
validators=[FileExtensionValidator(allowed_extensions=["py"])],
)
2024-03-19 04:53:35 +00:00
custom_checker_cpp = models.FileField(
verbose_name=_("custom cpp checker file"),
2022-05-14 17:57:27 +00:00
storage=problem_data_storage,
null=True,
blank=True,
upload_to=problem_directory_file,
validators=[FileExtensionValidator(allowed_extensions=["cpp"])],
)
interactive_judge = models.FileField(
verbose_name=_("interactive judge"),
storage=problem_data_storage,
null=True,
blank=True,
upload_to=problem_directory_file,
validators=[FileExtensionValidator(allowed_extensions=["cpp"])],
)
2022-06-02 04:59:46 +00:00
fileio_input = models.TextField(
2022-06-02 05:20:45 +00:00
verbose_name=_("input file name"),
blank=True,
null=True,
help_text=_("Leave empty for stdin"),
2022-06-02 04:59:46 +00:00
)
fileio_output = models.TextField(
2022-06-02 05:20:45 +00:00
verbose_name=_("output file name"),
blank=True,
null=True,
help_text=_("Leave empty for stdout"),
2022-06-02 04:59:46 +00:00
)
2023-03-10 04:31:55 +00:00
output_only = models.BooleanField(
verbose_name=_("is output only"),
help_text=_("Support output-only problem"),
null=True,
)
2023-08-01 05:26:15 +00:00
use_ioi_signature = models.BooleanField(
verbose_name=_("is IOI signature"),
help_text=_("Use IOI Signature"),
null=True,
)
signature_handler = models.FileField(
verbose_name=_("signature handler"),
storage=problem_data_storage,
null=True,
blank=True,
upload_to=problem_directory_file,
validators=[FileExtensionValidator(allowed_extensions=["cpp"])],
)
signature_header = models.FileField(
verbose_name=_("signature header"),
storage=problem_data_storage,
null=True,
blank=True,
upload_to=problem_directory_file,
validators=[FileExtensionValidator(allowed_extensions=["h"])],
)
2022-05-14 17:57:27 +00:00
2020-01-21 06:35:58 +00:00
__original_zipfile = None
def __init__(self, *args, **kwargs):
super(ProblemData, self).__init__(*args, **kwargs)
self.__original_zipfile = self.zipfile
def save(self, *args, **kwargs):
2022-05-08 01:06:39 +00:00
# Delete caches
if self.__original_zipfile:
2022-05-14 17:57:27 +00:00
try:
2021-06-01 20:34:42 +00:00
files = ZipFile(self.__original_zipfile.path).namelist()
for file in files:
2022-05-14 17:57:27 +00:00
cache_key = "problem_archive:%s:%s" % (
self.problem.code,
get_file_cachekey(file),
)
2021-06-01 20:34:42 +00:00
cache.delete(cache_key)
2023-10-27 23:02:02 +00:00
except (BadZipFile, FileNotFoundError):
2021-06-01 20:34:42 +00:00
pass
2023-10-27 23:02:02 +00:00
if self.zipfile != self.__original_zipfile:
self.__original_zipfile.delete(save=False)
2020-01-21 06:35:58 +00:00
return super(ProblemData, self).save(*args, **kwargs)
def has_yml(self):
2022-05-14 17:57:27 +00:00
return problem_data_storage.exists("%s/init.yml" % self.problem.code)
2020-01-21 06:35:58 +00:00
def _update_code(self, original, new):
if self.zipfile:
2022-09-22 03:21:05 +00:00
self.zipfile.name = problem_directory_file_helper(new, self.zipfile.name)
2020-01-21 06:35:58 +00:00
if self.generator:
2022-09-23 00:23:41 +00:00
self.generator.name = problem_directory_file_helper(
new, self.generator.name
)
2020-01-24 02:32:46 +00:00
if self.custom_checker:
2022-09-22 03:21:05 +00:00
self.custom_checker.name = problem_directory_file_helper(
2022-05-14 17:57:27 +00:00
new, self.custom_checker.name
)
if self.custom_checker:
2022-09-22 03:21:05 +00:00
self.custom_checker.name = problem_directory_file_helper(
2022-05-14 17:57:27 +00:00
new, self.custom_checker.name
)
2024-03-19 04:53:35 +00:00
if self.custom_checker_cpp:
self.custom_checker_cpp.name = problem_directory_file_helper(
new, self.custom_checker_cpp.name
2022-05-14 17:57:27 +00:00
)
2023-08-01 05:26:15 +00:00
if self.interactive_judge:
self.interactive_judge.name = problem_directory_file_helper(
new, self.interactive_judge.name
)
if self.signature_header:
self.signature_header.name = problem_directory_file_helper(
new, self.signature_header.name
)
if self.signature_handler:
self.signature_handler.name = problem_directory_file_helper(
new, self.signature_handler.name
)
2020-01-21 06:35:58 +00:00
self.save()
2022-05-14 17:57:27 +00:00
2020-01-21 06:35:58 +00:00
_update_code.alters_data = True
class ProblemTestCase(models.Model):
2022-05-14 17:57:27 +00:00
dataset = models.ForeignKey(
"Problem",
verbose_name=_("problem data set"),
related_name="cases",
on_delete=models.CASCADE,
)
order = models.IntegerField(verbose_name=_("case position"))
type = models.CharField(
max_length=1,
verbose_name=_("case type"),
choices=(
("C", _("Normal case")),
("S", _("Batch start")),
("E", _("Batch end")),
),
default="C",
)
input_file = models.CharField(
max_length=100, verbose_name=_("input file name"), blank=True
)
output_file = models.CharField(
max_length=100, verbose_name=_("output file name"), blank=True
)
generator_args = models.TextField(verbose_name=_("generator arguments"), blank=True)
points = models.IntegerField(verbose_name=_("point value"), blank=True, null=True)
is_pretest = models.BooleanField(verbose_name=_("case is pretest?"))
output_prefix = models.IntegerField(
verbose_name=_("output prefix length"), blank=True, null=True
)
output_limit = models.IntegerField(
verbose_name=_("output limit length"), blank=True, null=True
)
checker = models.CharField(
max_length=10, verbose_name=_("checker"), choices=CHECKERS, blank=True
)
checker_args = models.TextField(
verbose_name=_("checker arguments"),
blank=True,
help_text=_("checker arguments as a JSON object"),
)