Esempio n. 1
0
 def test_fields_autogeneration(self):
     contest = Contest()
     contest.save()
     self.assertEqual(contest.id, 'c1')
     round = Round(contest=contest)
     round.save()
     self.assertEqual(round.name, 'Round 1')
     round = Round(contest=contest)
     round.save()
     self.assertEqual(round.name, 'Round 2')
     problem = Problem()
     problem.save()
     pi = ProblemInstance(round=round, problem=problem)
     pi.save()
     self.assertEqual(pi.contest, contest)
Esempio n. 2
0
 def test_fields_autogeneration(self):
     contest = Contest()
     contest.save()
     self.assertEqual(contest.id, 'c1')
     round = Round(contest=contest)
     round.save()
     self.assertEqual(round.name, 'Round 1')
     round = Round(contest=contest)
     round.save()
     self.assertEqual(round.name, 'Round 2')
     problem = Problem()
     problem.save()
     pi = ProblemInstance(round=round, problem=problem)
     pi.save()
     self.assertEqual(pi.contest, contest)
Esempio n. 3
0
    def setUp(self):
        class MockRequest(object):
            def __init__(self):
                self.user = AnonymousUser()

        # mocking up a request below becouse I am NOT testing whole view
        self.request = MockRequest()

        for i in range(1, 5):
            name = 'problem_%s_name' % i
            url_key = 'problem_%s_key' % i
            problem = Problem(name=name, short_name=name, is_public=True)
            problem.save()
            site = ProblemSite(problem=problem, url_key=url_key)
            site.save()
Esempio n. 4
0
    def setUp(self):
        class MockRequest(object):
            def __init__(self):
                self.user = AnonymousUser()

        # mocking up a request below becouse I am NOT testing whole view
        self.request = MockRequest()

        for i in range(1, 5):
            name = 'problem_%s_name' % i
            url_key = 'problem_%s_key' % i
            problem = Problem(name=name, short_name=name, is_public=True)
            problem.save()
            site = ProblemSite(problem=problem, url_key=url_key)
            site.save()
Esempio n. 5
0
    def setUp(self):
        class MockRequest(object):
            def __init__(self):
                self.user = AnonymousUser()

        # mocking up a request below because I am NOT testing the whole view
        self.request = MockRequest()

        for i in range(1, 5):
            name = 'problem_%s_name' % i
            url_key = 'problem_%s_key' % i
            problem = Problem(name=name, short_name=name, visibility=Problem.VISIBILITY_PUBLIC)
            problem.save()
            site = ProblemSite(problem=problem, url_key=url_key)
            site.save()
Esempio n. 6
0
class ZeusPackage(SinolPackage):
    def _find_main_folder(self):
        files = map(os.path.normcase, self.archive.filenames())
        files = map(os.path.normpath, files)
        toplevel_folders = set(f.split(os.sep)[0] for f in files)
        toplevel_folders = filter(slug_re.match, toplevel_folders)

        folder = super(ZeusPackage, self)._find_main_folder()
        if not folder and len(toplevel_folders) > 0:
            folder = toplevel_folders[0]
        return folder

    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.zeus.controllers.'
                                   'ZeusProblemController')

        self.problem.package_backend_name = \
            'oioioi.zeus.package.ZeusPackageBackend'
        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._process_model_solutions()
            self._save_original_package()
            return self.problem
        finally:
            shutil.rmtree(tmpdir)
Esempio n. 7
0
class SinolPackage(object):
    def __init__(self, path, original_filename=None):
        self.filename = original_filename or path
        if self.filename.lower().endswith('.tar.gz'):
            ext = '.tar.gz'
        else:
            ext = os.path.splitext(self.filename)[1]
        self.archive = Archive(path, ext)
        self.config = None
        self.problem = None
        self.rootdir = None
        self.short_name = self._find_main_folder()

    def _find_main_folder(self):
        # Looks for the only folder which has at least the in/ and out/
        # subfolders.
        #
        # Note that depending on the archive type, there may be or
        # may not be entries for the folders themselves in
        # self.archive.filenames()

        files = map(os.path.normcase, self.archive.filenames())
        files = map(os.path.normpath, files)
        toplevel_folders = set(f.split(os.sep)[0] for f in files)
        toplevel_folders = filter(slug_re.match, toplevel_folders)
        problem_folders = []
        for folder in toplevel_folders:
            for required_subfolder in ('in', 'out'):
                if all(f.split(os.sep)[:2] != [folder, required_subfolder]
                       for f in files):
                    break
            else:
                problem_folders.append(folder)
        if len(problem_folders) == 1:
            return problem_folders[0]

    def identify(self):
        return self._find_main_folder() is not None

    def _process_config_yml(self):
        config_file = os.path.join(self.rootdir, 'config.yml')
        instance, created = \
                ExtraConfig.objects.get_or_create(problem=self.problem)
        if os.path.exists(config_file):
            instance.config = open(config_file, 'r').read()
        else:
            instance.config = ''
        instance.save()
        self.config = instance.parsed_config

    def _detect_full_name(self):
        """Sets the problem's full name from the ``config.yml`` (key ``title``)
           or from the ``title`` tag in the LateX source file.

           Example of how the ``title`` tag may look like:
           \title{A problem}
        """
        if 'title' in self.config:
           self.problem.name = self.config['title']
           self.problem.save()
           return

        source = os.path.join(self.rootdir, 'doc', self.short_name + 'zad.tex')
        if os.path.isfile(source):
            text = open(source, 'r').read()
            r = re.search(r'\\title{(.+)}', text)
            if r is not None:
                self.problem.name = _decode(r.group(1), text)
                self.problem.save()

    def _compile_docs(self, docdir):
        # fancyheadings.sty looks like a rarely available LaTeX package...
        src_fancyheadings = os.path.join(os.path.dirname(__file__), 'files',
                'fancyheadings.sty')
        dst_fancyheadings = os.path.join(docdir, 'fancyheadings.sty')
        if not os.path.exists(dst_fancyheadings):
            shutil.copyfile(src_fancyheadings, dst_fancyheadings)

        # Extract sinol.cls and oilogo.*, but do not overwrite if they
        # already exist (-k).
        sinol_cls_tgz = os.path.join(os.path.dirname(__file__), 'files',
                'sinol-cls.tgz')
        execute(['tar', '-C', docdir, '-kzxf', sinol_cls_tgz], cwd=docdir)

        try:
            execute('make', cwd=docdir)
        except ExecuteError:
            logger.warning('%s: failed to compile statement', self.filename,
                    exc_info=True)

    def _process_statements(self):
        docdir = os.path.join(self.rootdir, 'doc')
        if not os.path.isdir(docdir):
            logger.warning('%s: no docdir', self.filename)
            return

        self.problem.statements.all().delete()

        htmlzipfile = os.path.join(docdir, self.short_name + 'zad.html.zip')
        if os.path.exists(htmlzipfile):
            statement = ProblemStatement(problem=self.problem)
            statement.content.save(self.short_name + '.html.zip',
                    File(open(htmlzipfile, 'rb')))

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

        if not os.path.isfile(pdffile):
            self._compile_docs(docdir)
        if not os.path.isfile(pdffile):
            logger.warning('%s: no problem statement', self.filename)
            return

        statement = ProblemStatement(problem=self.problem)
        statement.content.save(self.short_name + '.pdf',
                File(open(pdffile, 'rb')))

    def _extract_makefiles(self):
        sinol_makefiles_tgz = os.path.join(os.path.dirname(__file__),
                'files', 'sinol-makefiles.tgz')
        Archive(sinol_makefiles_tgz).extract(to_path=self.rootdir)

        makefile_in = os.path.join(self.rootdir, 'makefile.in')
        if not os.path.exists(makefile_in):
           with open(makefile_in, 'w') as f:
                f.write('MODE=wer\n')
                f.write('ID=%s\n' % (self.short_name,))
                f.write('SIG=xxxx000\n')

    def _generate_tests(self):
        logger.info('%s: ingen', self.filename)
        execute('make ingen', cwd=self.rootdir)

        if glob.glob(os.path.join(self.rootdir, 'prog',
                '%sinwer.*' % (self.short_name,))):
            logger.info('%s: inwer', self.filename)
            execute('make inwer', cwd=self.rootdir)
        else:
            logger.info('%s: no inwer in package', self.filename)

        indir = os.path.join(self.rootdir, 'in')
        outdir = os.path.join(self.rootdir, 'out')
        for test in os.listdir(indir):
            basename = os.path.splitext(test)[0]
            if not os.path.exists(os.path.join(outdir, basename + '.out')):
                logger.info('%s: outgen', self.filename)
                execute('make outgen', cwd=self.rootdir)
                break

    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_checkers(self):
        checker_prefix = os.path.join(self.rootdir, 'prog',
                self.short_name + 'chk')
        checker = None

        source_candidates = [
                checker_prefix + '.cpp',
                checker_prefix + '.c',
                checker_prefix + '.pas',
            ]
        for source in source_candidates:
            if os.path.isfile(source):
                logger.info('%s: compiling checker', self.filename)
                execute(['make', self.short_name + 'chk.e'],
                        cwd=os.path.join(self.rootdir, 'prog'))
                break

        exe_candidates = [
                checker_prefix + '.e',
                checker_prefix + '.sh',
            ]
        for exe in exe_candidates:
            if os.path.isfile(exe):
                checker = exe

        instance = OutputChecker.objects.get(problem=self.problem)
        if checker:
            instance.exe_file.save(os.path.basename(checker),
                File(open(checker, 'rb')))
        else:
            instance.exe_file = None
            instance.save()

    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')))

    def _process_model_solutions(self):
        ModelSolution.objects.filter(problem=self.problem).delete()

        names_re = re.compile(r'^%s[0-9]*([bs]?)[0-9]*\.(c|cpp|pas|java)'
                % (re.escape(self.short_name),))
        progdir = os.path.join(self.rootdir, 'prog')
        for name in os.listdir(progdir):
            path = os.path.join(progdir, name)
            if not os.path.isfile(path):
                continue
            match = names_re.match(name)
            if match:
                instance = ModelSolution(problem=self.problem, name=name)
                instance.kind = {
                        '': 'NORMAL',
                        's': 'SLOW',
                        'b': 'INCORRECT',
                    }[match.group(1)]
                instance.source_file.save(name, File(open(path, 'rb')))
                logger.info('%s: model solution: %s', self.filename, name)

    def _save_original_package(self):
        original_package, created = \
                OriginalPackage.objects.get_or_create(problem=self.problem)
        original_package.package_file.save(os.path.basename(self.filename),
                File(open(self.archive.filename, 'rb')))

    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)
Esempio n. 8
0
class SinolPackage(object):
    def __init__(self, path, original_filename=None):
        self.filename = original_filename or path
        if self.filename.lower().endswith('.tar.gz'):
            ext = '.tar.gz'
        else:
            ext = os.path.splitext(self.filename)[1]
        self.archive = Archive(path, ext)
        self.config = None
        self.problem = None
        self.rootdir = None
        self.short_name = self._find_main_folder()

    def _find_main_folder(self):
        # Looks for the only folder which has at least the in/ and out/
        # subfolders.
        #
        # Note that depending on the archive type, there may be or
        # may not be entries for the folders themselves in
        # self.archive.filenames()

        files = map(os.path.normcase, self.archive.filenames())
        files = map(os.path.normpath, files)
        toplevel_folders = set(f.split(os.sep)[0] for f in files)
        toplevel_folders = filter(slug_re.match, toplevel_folders)
        problem_folders = []
        for folder in toplevel_folders:
            for required_subfolder in ('in', 'out'):
                if all(
                        f.split(os.sep)[:2] != [folder, required_subfolder]
                        for f in files):
                    break
            else:
                problem_folders.append(folder)
        if len(problem_folders) == 1:
            return problem_folders[0]

    def identify(self):
        return self._find_main_folder() is not None

    def _process_config_yml(self):
        config_file = os.path.join(self.rootdir, 'config.yml')
        instance, created = \
                ExtraConfig.objects.get_or_create(problem=self.problem)
        if os.path.exists(config_file):
            instance.config = open(config_file, 'r').read()
        else:
            instance.config = ''
        instance.save()
        self.config = instance.parsed_config

    def _detect_full_name(self):
        """Sets the problem's full name from the ``config.yml`` (key ``title``)
           or from the ``title`` tag in the LateX source file.

           Example of how the ``title`` tag may look like:
           \title{A problem}
        """
        if 'title' in self.config:
            self.problem.name = self.config['title']
            self.problem.save()
            return

        source = os.path.join(self.rootdir, 'doc', self.short_name + 'zad.tex')
        if os.path.isfile(source):
            text = open(source, 'r').read()
            r = re.search(r'\\title{(.+)}', text)
            if r is not None:
                self.problem.name = _decode(r.group(1), text)
                self.problem.save()

    def _compile_docs(self, docdir):
        # fancyheadings.sty looks like a rarely available LaTeX package...
        src_fancyheadings = os.path.join(os.path.dirname(__file__), 'files',
                                         'fancyheadings.sty')
        dst_fancyheadings = os.path.join(docdir, 'fancyheadings.sty')
        if not os.path.exists(dst_fancyheadings):
            shutil.copyfile(src_fancyheadings, dst_fancyheadings)

        # Extract sinol.cls and oilogo.*, but do not overwrite if they
        # already exist (-k).
        sinol_cls_tgz = os.path.join(os.path.dirname(__file__), 'files',
                                     'sinol-cls.tgz')
        execute(['tar', '-C', docdir, '-kzxf', sinol_cls_tgz], cwd=docdir)

        try:
            execute('make', cwd=docdir)
        except ExecuteError:
            logger.warning('%s: failed to compile statement',
                           self.filename,
                           exc_info=True)

    def _process_statements(self):
        docdir = os.path.join(self.rootdir, 'doc')
        if not os.path.isdir(docdir):
            logger.warning('%s: no docdir', self.filename)
            return

        self.problem.statements.all().delete()

        htmlzipfile = os.path.join(docdir, self.short_name + 'zad.html.zip')
        if os.path.exists(htmlzipfile):
            statement = ProblemStatement(problem=self.problem)
            statement.content.save(self.short_name + '.html.zip',
                                   File(open(htmlzipfile, 'rb')))

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

        if not os.path.isfile(pdffile):
            self._compile_docs(docdir)
        if not os.path.isfile(pdffile):
            logger.warning('%s: no problem statement', self.filename)
            return

        statement = ProblemStatement(problem=self.problem)
        statement.content.save(self.short_name + '.pdf',
                               File(open(pdffile, 'rb')))

    def _extract_makefiles(self):
        sinol_makefiles_tgz = os.path.join(os.path.dirname(__file__), 'files',
                                           'sinol-makefiles.tgz')
        Archive(sinol_makefiles_tgz).extract(to_path=self.rootdir)

        makefile_in = os.path.join(self.rootdir, 'makefile.in')
        if not os.path.exists(makefile_in):
            with open(makefile_in, 'w') as f:
                f.write('MODE=wer\n')
                f.write('ID=%s\n' % (self.short_name, ))
                f.write('SIG=xxxx000\n')

    def _generate_tests(self):
        logger.info('%s: ingen', self.filename)
        execute('make ingen', cwd=self.rootdir)

        if glob.glob(
                os.path.join(self.rootdir, 'prog',
                             '%sinwer.*' % (self.short_name, ))):
            logger.info('%s: inwer', self.filename)
            execute('make inwer', cwd=self.rootdir)
        else:
            logger.info('%s: no inwer in package', self.filename)

        indir = os.path.join(self.rootdir, 'in')
        outdir = os.path.join(self.rootdir, 'out')
        for test in os.listdir(indir):
            basename = os.path.splitext(test)[0]
            if not os.path.exists(os.path.join(outdir, basename + '.out')):
                logger.info('%s: outgen', self.filename)
                execute('make outgen', cwd=self.rootdir)
                break

    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_checkers(self):
        checker_prefix = os.path.join(self.rootdir, 'prog',
                                      self.short_name + 'chk')
        checker = None

        source_candidates = [
            checker_prefix + '.cpp',
            checker_prefix + '.c',
            checker_prefix + '.pas',
        ]
        for source in source_candidates:
            if os.path.isfile(source):
                logger.info('%s: compiling checker', self.filename)
                execute(['make', self.short_name + 'chk.e'],
                        cwd=os.path.join(self.rootdir, 'prog'))
                break

        exe_candidates = [
            checker_prefix + '.e',
            checker_prefix + '.sh',
        ]
        for exe in exe_candidates:
            if os.path.isfile(exe):
                checker = exe

        instance = OutputChecker.objects.get(problem=self.problem)
        if checker:
            instance.exe_file.save(os.path.basename(checker),
                                   File(open(checker, 'rb')))
        else:
            instance.exe_file = None
            instance.save()

    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')))

    def _process_model_solutions(self):
        ModelSolution.objects.filter(problem=self.problem).delete()

        names_re = re.compile(r'^%s[0-9]*([bs]?)[0-9]*\.(c|cpp|pas|java)' %
                              (re.escape(self.short_name), ))
        progdir = os.path.join(self.rootdir, 'prog')
        for name in os.listdir(progdir):
            path = os.path.join(progdir, name)
            if not os.path.isfile(path):
                continue
            match = names_re.match(name)
            if match:
                instance = ModelSolution(problem=self.problem, name=name)
                instance.kind = {
                    '': 'NORMAL',
                    's': 'SLOW',
                    'b': 'INCORRECT',
                }[match.group(1)]
                instance.source_file.save(name, File(open(path, 'rb')))
                logger.info('%s: model solution: %s', self.filename, name)

    def _save_original_package(self):
        original_package, created = \
                OriginalPackage.objects.get_or_create(problem=self.problem)
        original_package.package_file.save(
            os.path.basename(self.filename),
            File(open(self.archive.filename, 'rb')))

    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)