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(code, filename): return os.path.join(code, os.path.basename(filename)) def problem_directory_file(data, filename): return _problem_directory_file(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)')), ('customval', _('Custom validator (CPP)')), ) 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_validator = models.FileField(verbose_name=_('custom validator file'), storage=problem_data_storage, null=True, blank=True, upload_to=problem_directory_file, validators=[FileExtensionValidator(allowed_extensions=['cpp'])]) __original_zipfile = None def __init__(self, *args, **kwargs): super(ProblemData, self).__init__(*args, **kwargs) self.__original_zipfile = self.zipfile def save(self, *args, **kwargs): if self.zipfile != self.__original_zipfile and self.__original_zipfile: # Delete caches 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: pass 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): try: problem_data_storage.rename(original, new) except OSError as e: if e.errno != errno.ENOENT: raise if self.zipfile: self.zipfile.name = _problem_directory_file(new, self.zipfile.name) if self.generator: self.generator.name = _problem_directory_file(new, self.generator.name) if self.custom_checker: self.custom_checker.name = _problem_directory_file(new, self.custom_checker.name) if self.custom_checker: self.custom_checker.name = _problem_directory_file(new, self.custom_checker.name) if self.custom_validator: self.custom_validator.name = _problem_directory_file(new, self.custom_validator.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'))