Пример #1
0
    def _check_scores_from_config(self, scored_groups, config_scores):
        """Called if ``config.yml`` specifies scores for any tests.
        Makes sure that all scored tests are present in ``config.yml``
        and that nothing else is there.
        """

        for group in scored_groups:
            if int(group) not in config_scores:
                errormsg = _("Score for group '%(group_name)s' not found. "
                             "You must either provide scores for all groups "
                             "or not provide them at all "
                             "(to have them assigned automatically). "
                             "(Scored groups: %(scored_groups)s, "
                             "groups from config: %(config_groups)s)") % {
                                 "group_name": group,
                                 "scored_groups": list(scored_groups),
                                 "config_groups": config_scores,
                             }
                raise ProblemPackageError(errormsg)

        for group in config_scores:
            if str(group) not in scored_groups:
                errormsg = _("Score for group '%(group_name)s' "
                             "found in config, "
                             "but no such test group exists in scored groups. "
                             "You must either provide scores for all groups "
                             "or not provide them at all "
                             "(to have them assigned automatically). "
                             "(Scored groups: %(scored_groups)s, "
                             "groups from config: %(config_groups)s)") % {
                                 "group_name": group,
                                 "scored_groups": list(scored_groups),
                                 "config_groups": config_scores,
                             }
                raise ProblemPackageError(errormsg)
Пример #2
0
 def _validate_tests(self, created_tests):
     """Check if all tests have output files and that
        test instances are correct.
        :raises: :class:`~oioioi.problems.package.ProblemPackageError`
     """
     for instance in created_tests:
         if not instance.output_file:
             raise ProblemPackageError(
                 _("Missing out file for test %s") % instance.name)
         try:
             instance.full_clean()
         except ValidationError as e:
             raise ProblemPackageError(e.messages[0])
Пример #3
0
    def _verify_inputs(self, tests):
        """Check if correct solution exits with code 0 on all tests.
           :raises: :class:`~oioioi.problems.package.ProblemPackageError`
           otherwise.
        """
        env = self._find_and_compile('inwer')
        if env and not self.use_make:
            jobs = {}

            for test in tests:
                job = env.copy()
                job['job_type'] = 'inwer'
                job['task_priority'] = TASK_PRIORITY
                job['exe_file'] = env['compiled_file']
                job['in_file'] = django_to_filetracker_path(test.input_file)
                job['use_sandboxes'] = self.use_sandboxes
                jobs[test.name] = job

            jobs = run_sioworkers_jobs(jobs)
            get_client().delete_file(env['compiled_file'])

            for test_name, job in jobs.iteritems():
                if job['result_code'] != 'OK':
                    raise ProblemPackageError(
                        _("Inwer failed on test "
                          "%(test)s. Inwer output %(output)s") % {
                              'test': test_name,
                              'output': '\n'.join(job['stdout'])
                          })

            logger.info("%s: inwer success", self.filename)
Пример #4
0
    def _force_index_encoding(self, htmlzipfile):
        """Ensures index.html file is utf-8 encoded, if cannot apply
           this encoding raise
           :class:`~oioioi.problems.package.ProblemPackageError`.
        """
        with zipfile.ZipFile(htmlzipfile, 'r') as archive, \
                archive.open('index.html') as index:

            data = index.read()
            # First, we check if index.html is utf-8 encoded.
            # If it is - nothing to do.
            try:
                data.decode('utf8')
            # We accept iso-8859-2 encoded files, but django doesn't
            # so index.html has to be translated to utf-8.
            except UnicodeDecodeError:
                try:
                    data = data.decode('iso-8859-2').encode('utf8')
                except (UnicodeDecodeError, UnicodeEncodeError):
                    raise ProblemPackageError(
                        _("index.html has to be utf8 or iso8859-2 encoded"))
                # We have to remove index.html from the archive and
                # then add the translated file to archive because
                # zipfile module doesn't implement editing files
                # inside archive.
                _remove_from_zip(htmlzipfile, 'index.html')
                with zipfile.ZipFile(htmlzipfile, 'a') as new_archive:
                    new_archive.writestr('index.html', data)
Пример #5
0
 def _process_extra_files(self):
     ExtraFile.objects.filter(problem=self.problem).delete()
     files = list(self.config.get('extra_compilation_files', ()))
     not_found = self._find_and_save_files(files)
     if not_found:
         raise ProblemPackageError(
             _("Expected extra files %r not found in prog/") % (not_found))
Пример #6
0
 def _ensure_short_name_equality_with(self, existing_problem):
     if existing_problem.short_name != self.short_name:
         raise ProblemPackageError(_("Tried to replace problem "
             "'%(oldname)s' with '%(newname)s'. For safety, changing "
             "problem short name is not possible.") %
             dict(oldname=existing_problem.short_name,
                  newname=self.short_name))
Пример #7
0
 def _process_extra_files(self):
     ExtraFile.objects.filter(problem=self.problem).delete()
     for filename in self.config.get('extra_compilation_files', ()):
         fn = os.path.join(self.rootdir, 'prog', filename)
         if not os.path.exists(fn):
             raise ProblemPackageError(
                 _("Expected extra file '%s' not "
                   "found in prog/") % (filename, ))
         instance = ExtraFile(problem=self.problem, name=filename)
         instance.file.save(filename, File(open(fn, 'rb')))
Пример #8
0
    def _ensure_compilation_success(self, filename, new_env):
        compilation_message = new_env.get('compiler_output', '')
        compilation_result = new_env.get('result_code', 'CE')
        if compilation_result != 'OK':
            logger.warning("%s: compilation of file %s failed with code %s",
                           self.filename, filename, compilation_result)
            logger.warning("%s: compiler output: %r", self.filename,
                           compilation_message)

            raise ProblemPackageError(_("Compilation of file %(filename)s "
                                        "failed. Compiler output: "
                                        "%(output)s") % {
                                          'filename': filename,
                                          'output': compilation_message})
Пример #9
0
    def _verify_time_limits(self, tests):
        """:raises: :class:`~oioioi.problems.package.ProblemPackageError`
           if sum of tests time limits exceeds
        """
        time_limit_sum = 0
        for test in tests:
            time_limit_sum += test.time_limit
        if time_limit_sum > settings.MAX_TEST_TIME_LIMIT_PER_PROBLEM:
            time_limit_sum_rounded = (time_limit_sum + 999) / 1000.0
            limit_seconds = settings.MAX_TEST_TIME_LIMIT_PER_PROBLEM / 1000.0

            raise ProblemPackageError(_(
                "Sum of time limits for all tests is too big. It's %(sum)ds, "
                "but it shouldn't exceed %(limit)ds."
            ) % {'sum': time_limit_sum_rounded, 'limit': limit_seconds})
Пример #10
0
    def _process_statements(self):
        """Creates a problem statement from html or pdf source.

        If `USE_SINOLPACK_MAKEFILES` is set to True in the OIOIOI settings,
        the pdf file will be compiled from a LaTeX source.
        """
        docdir = os.path.join(self.rootdir, 'doc')
        if not os.path.isdir(docdir):
            logger.warning("%s: no docdir", self.filename)
            return

        # pylint: disable=maybe-no-member
        self.problem.statements.all().delete()

        lang_prefs = [''] + ['-' + l[0] for l in settings.STATEMENT_LANGUAGES]

        if self.use_make:
            self._compile_latex_docs(docdir)

        for lang in lang_prefs:
            htmlzipfile = os.path.join(
                docdir, self.short_name + 'zad' + lang + '.html.zip')
            if os.path.isfile(htmlzipfile):
                if self._html_disallowed():
                    raise ProblemPackageError(
                        _("You cannot upload package with "
                          "problem statement in HTML. "
                          "Try again using PDF format."))

                self._force_index_encoding(htmlzipfile)
                statement = ProblemStatement(problem=self.problem,
                                             language=lang[1:])
                statement.content.save(self.short_name + lang + '.html.zip',
                                       File(open(htmlzipfile, 'rb')))

            pdffile = os.path.join(docdir,
                                   self.short_name + 'zad' + lang + '.pdf')

            if os.path.isfile(pdffile):
                statement = ProblemStatement(problem=self.problem,
                                             language=lang[1:])
                statement.content.save(self.short_name + lang + '.pdf',
                                       File(open(pdffile, 'rb')))

        if not self.problem.statements.exists():
            logger.warning("%s: no problem statement", self.filename)
Пример #11
0
    def unpack(self, existing_problem=None):
        self.short_name = self._find_main_folder()

        if existing_problem:
            self.problem = existing_problem
            if existing_problem.short_name != self.short_name:
                raise ProblemPackageError(
                    _("Tried to replace problem "
                      "'%(oldname)s' with '%(newname)s'. For safety, changing "
                      "problem short name is not possible.") %
                    dict(oldname=existing_problem.short_name,
                         newname=self.short_name))
        else:
            self.problem = Problem(
                name=self.short_name,
                short_name=self.short_name,
                controller_name=
                'oioioi.sinolpack.controllers.SinolProblemController')

        self.problem.package_backend_name = \
                'oioioi.sinolpack.package.SinolPackageBackend'
        self.problem.save()

        tmpdir = tempfile.mkdtemp()
        logger.info('%s: tmpdir is %s', self.filename, tmpdir)
        try:
            self.archive.extract(to_path=tmpdir)
            self.rootdir = os.path.join(tmpdir, self.short_name)
            self._process_config_yml()
            self._detect_full_name()
            self._extract_makefiles()
            self._process_statements()
            self._generate_tests()
            self._process_tests()
            self._process_checkers()
            self._process_extra_files()
            self._process_model_solutions()
            self._save_original_package()
            return self.problem
        finally:
            shutil.rmtree(tmpdir)
Пример #12
0
    def _process_test(self, test, order, names_re, indir, outdir,
                      collected_ins, scored_groups, outs_to_make):
        """Responsible for saving test in and out files,
           setting test limits, assigning test kind and group.
           :param test: Test name.
           :param order: Test number.
           :param names_re: Compiled regex to match test details from name.
                  Should extract basename, test name,
                  group number and test type.
           :param indir: Directory with tests inputs.
           :param outdir: Directory with tests outputs.
           :param collected_ins: List of inputs that were generated,
                  not taken from archive as a file.
           :param scored_groups: Accumulator for score groups.
           :param outs_to_make: Accumulator for name of output files to
                  be generated by model solution.
           :return: Test instance or None if name couldn't be matched.
        """
        match = names_re.match(test)
        if not match:
            if test.endswith('.in'):
                raise ProblemPackageError(_("Unrecognized test: %s") % (test))
            return None

        # Examples for odl0ocen.in:
        basename = match.group(1)  # odl0ocen
        name = match.group(2)  # 0ocen
        group = match.group(3)  # 0
        suffix = match.group(4)  # ocen

        instance, created = Test.objects.get_or_create(
            problem_instance=self.main_problem_instance, name=name)

        inname_base = basename + '.in'
        inname = os.path.join(indir, inname_base)
        outname_base = basename + '.out'
        outname = os.path.join(outdir, outname_base)

        if test in collected_ins:
            self._save_to_field(instance.input_file, collected_ins[test])
        else:
            instance.input_file.save(inname_base, File(open(inname, 'rb')))

        if os.path.isfile(outname):
            instance.output_file.save(outname_base, File(open(outname), 'rb'))

        outs_to_make.append(
            (_make_filename_in_job_dir(self.env,
                                       'out/%s' % (outname_base)), instance))

        if group == '0' or 'ocen' in suffix:
            # Example tests
            instance.kind = 'EXAMPLE'
            instance.group = name
        else:
            instance.kind = 'NORMAL'
            instance.group = group
            scored_groups.add(group)

        if created:
            instance.time_limit = self.time_limits.get(name,
                                                       DEFAULT_TIME_LIMIT)

        memory_limit = self._get_memory_limit(created, name)
        if memory_limit:
            instance.memory_limit = memory_limit

        instance.order = order
        instance.save()
        return instance
Пример #13
0
    def _process_tests(self, total_score=100):
        indir = os.path.join(self.rootdir, 'in')
        outdir = os.path.join(self.rootdir, 'out')
        test_names = []
        scored_groups = set()
        names_re = re.compile(r'^(%s(([0-9]+)([a-z]?[a-z0-9]*))).in$' %
                              (re.escape(self.short_name), ))

        time_limits = _stringify_keys(self.config.get('time_limits', {}))
        memory_limits = _stringify_keys(self.config.get('memory_limits', {}))

        # Find tests and create objects
        for order, test in enumerate(
                sorted(os.listdir(indir), key=naturalsort_key)):
            match = names_re.match(test)
            if not match:
                if test.endswith('.in'):
                    raise ProblemPackageError("Unrecognized test: " + test)
                continue

            # Examples for odl0ocen.in:
            basename = match.group(1)  # odl0ocen
            name = match.group(2)  # 0ocen
            group = match.group(3)  # 0
            suffix = match.group(4)  # ocen

            instance, created = Test.objects.get_or_create(
                problem=self.problem, name=name)
            instance.input_file.save(
                basename + '.in',
                File(open(os.path.join(indir, basename + '.in'), 'rb')))
            instance.output_file.save(
                basename + '.out',
                File(open(os.path.join(outdir, basename + '.out'), 'rb')))
            if group == '0' or 'ocen' in suffix:
                # Example tests
                instance.kind = 'EXAMPLE'
                instance.group = name
            else:
                instance.kind = 'NORMAL'
                instance.group = group
                scored_groups.add(group)

            if created:
                instance.time_limit = time_limits.get(name, DEFAULT_TIME_LIMIT)
                if 'memory_limit' in self.config:
                    instance.memory_limit = self.config['memory_limit']
                else:
                    instance.memory_limit = memory_limits.get(
                        name, DEFAULT_MEMORY_LIMIT)
            instance.order = order
            instance.save()
            test_names.append(name)

        # Delete nonexistent tests
        for test in Test.objects.filter(problem=self.problem) \
                .exclude(name__in=test_names):
            logger.info('%s: deleting test %s', self.filename, test.name)
            test.delete()

        # Assign scores
        if scored_groups:
            Test.objects.filter(problem=self.problem).update(max_score=0)
            num_groups = len(scored_groups)
            group_score = total_score / num_groups
            extra_score_groups = sorted(
                scored_groups,
                key=naturalsort_key)[num_groups -
                                     (total_score - num_groups * group_score):]
            for group in scored_groups:
                score = group_score
                if group in extra_score_groups:
                    score += 1
                Test.objects.filter(problem=self.problem, group=group) \
                        .update(max_score=score)
    def _process_tests(self, total_score=100):
        indir = os.path.join(self.rootdir, 'in')
        outdir = os.path.join(self.rootdir, 'out')
        test_names = []
        scored_groups = set()
        names_re = re.compile(r'^(%s(([0-9]+)([a-z]?[a-z0-9]*))).in$' %
                              (re.escape(self.short_name), ))

        time_limits = _stringify_keys(self.config.get('time_limits', {}))
        memory_limits = _stringify_keys(self.config.get('memory_limits', {}))
        statement_memory_limit = self._detect_statement_memory_limit()

        # Find tests and create objects
        for order, test in enumerate(
                sorted(os.listdir(indir), key=naturalsort_key)):
            match = names_re.match(test)
            if not match:
                if test.endswith('.in'):
                    raise ProblemPackageError("Unrecognized test: " + test)
                continue

            # Examples for odl0ocen.in:
            basename = match.group(1)  # odl0ocen
            name = match.group(2)  # 0ocen
            group = match.group(3)  # 0
            suffix = match.group(4)  # ocen

            instance, created = Test.objects.get_or_create(
                problem=self.problem, name=name)
            instance.input_file.save(
                basename + '.in',
                File(open(os.path.join(indir, basename + '.in'), 'rb')))
            instance.output_file.save(
                basename + '.out',
                File(open(os.path.join(outdir, basename + '.out'), 'rb')))
            if group == '0' or 'ocen' in suffix:
                # Example tests
                instance.kind = 'EXAMPLE'
                instance.group = name
            else:
                instance.kind = 'NORMAL'
                instance.group = group
                scored_groups.add(group)

            if created:
                instance.time_limit = time_limits.get(name, DEFAULT_TIME_LIMIT)

            # If we find the memory limit specified anywhere in the package:
            # either in the config.yml or in the problem statement, then we
            # overwrite potential manual changes. (In the future we should
            # disallow editing memory limits if they were taken from the
            # package).
            if name in memory_limits:
                instance.memory_limit = memory_limits[name]
            elif 'memory_limit' in self.config:
                instance.memory_limit = self.config['memory_limit']
            elif statement_memory_limit is not None:
                instance.memory_limit = statement_memory_limit
            elif created:
                instance.memory_limit = DEFAULT_MEMORY_LIMIT

            instance.order = order
            try:
                instance.full_clean()
            except ValidationError as e:
                raise ProblemPackageError(e.messages[0])
            instance.save()
            test_names.append(name)

        # Delete nonexistent tests
        for test in Test.objects.filter(problem=self.problem) \
                .exclude(name__in=test_names):
            logger.info('%s: deleting test %s', self.filename, test.name)
            test.delete()

        # Assign scores
        if scored_groups:
            Test.objects.filter(problem=self.problem).update(max_score=0)
            num_groups = len(scored_groups)
            group_score = total_score / num_groups
            extra_score_groups = sorted(
                scored_groups,
                key=naturalsort_key)[num_groups -
                                     (total_score - num_groups * group_score):]
            for group in scored_groups:
                score = group_score
                if group in extra_score_groups:
                    score += 1
                Test.objects.filter(problem=self.problem, group=group) \
                        .update(max_score=score)