NDOJ/judge/models/problem_data.py
2024-03-18 23:53:35 -05:00

250 lines
8.2 KiB
Python

import errno
import os
from zipfile import BadZipFile, ZipFile
from django.core.validators import FileExtensionValidator
from django.core.cache import cache
from django.db import models
from django.utils.translation import gettext_lazy as _
from judge.utils.problem_data import ProblemDataStorage, get_file_cachekey
__all__ = [
"problem_data_storage",
"problem_directory_file",
"ProblemData",
"ProblemTestCase",
"CHECKERS",
]
problem_data_storage = ProblemDataStorage()
def problem_directory_file_helper(code, filename):
return os.path.join(code, os.path.basename(filename))
def problem_directory_file(data, filename):
return problem_directory_file_helper(data.problem.code, filename)
CHECKERS = (
("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)")),
("customcpp", _("Custom checker (CPP)")),
("interact", _("Interactive")),
("testlib", _("Testlib")),
)
class ProblemData(models.Model):
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"])],
)
custom_checker_cpp = models.FileField(
verbose_name=_("custom cpp checker file"),
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"])],
)
fileio_input = models.TextField(
verbose_name=_("input file name"),
blank=True,
null=True,
help_text=_("Leave empty for stdin"),
)
fileio_output = models.TextField(
verbose_name=_("output file name"),
blank=True,
null=True,
help_text=_("Leave empty for stdout"),
)
output_only = models.BooleanField(
verbose_name=_("is output only"),
help_text=_("Support output-only problem"),
null=True,
)
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"])],
)
__original_zipfile = None
def __init__(self, *args, **kwargs):
super(ProblemData, self).__init__(*args, **kwargs)
self.__original_zipfile = self.zipfile
def save(self, *args, **kwargs):
# Delete caches
if self.__original_zipfile:
try:
files = ZipFile(self.__original_zipfile.path).namelist()
for file in files:
cache_key = "problem_archive:%s:%s" % (
self.problem.code,
get_file_cachekey(file),
)
cache.delete(cache_key)
except (BadZipFile, FileNotFoundError):
pass
if self.zipfile != self.__original_zipfile:
self.__original_zipfile.delete(save=False)
return super(ProblemData, self).save(*args, **kwargs)
def has_yml(self):
return problem_data_storage.exists("%s/init.yml" % self.problem.code)
def _update_code(self, original, new):
if self.zipfile:
self.zipfile.name = problem_directory_file_helper(new, self.zipfile.name)
if self.generator:
self.generator.name = problem_directory_file_helper(
new, self.generator.name
)
if self.custom_checker:
self.custom_checker.name = problem_directory_file_helper(
new, self.custom_checker.name
)
if self.custom_checker:
self.custom_checker.name = problem_directory_file_helper(
new, self.custom_checker.name
)
if self.custom_checker_cpp:
self.custom_checker_cpp.name = problem_directory_file_helper(
new, self.custom_checker_cpp.name
)
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
)
self.save()
_update_code.alters_data = True
class ProblemTestCase(models.Model):
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"),
)