Exemple #1
0
    def __init__(self, *args, **kwargs):
        bid = 1000 + (os.getpid() % 8999) # 8999 is prime

        IsolateSandbox.__init__(self, *args, box_id=bid, **kwargs)

        self.preserve_env = True
        self.max_processes = config.latex_compilation_sandbox_max_processes
        self.timeout = config.latex_compilation_sandbox_max_time_s
        self.wallclock_timeout = 2 * self.timeout + 1
        self.address_space = config.latex_compilation_sandbox_max_memory_kib * 1024

        self.stdout_file = "LaTeX_out.txt"
        self.stderr_file = "LaTeX_err.txt"
        self.add_mapped_directory("/etc/texmf")
        self.add_mapped_directory("/var/lib/texmf")
        self.add_mapped_directory("/etc/fonts")
        # /usr is mapped per default, so we don't need to
        # map anything from there explicitly.

        for d in config.latex_additional_dirs:
            # We probably can't map the directory into the sandbox's
            # home directory, so we copy it there.
            copyrecursivelyifnecessary(os.path.expanduser(d),
                                    os.path.join(self.get_home_path(),
                                                 d[2:] if d[:2]=="~/" else d),
                                    mode=0o777)
Exemple #2
0
    def ontask(self, task):
        """ Some additional supplies for the latex format
        """
        super(BOITemplate, self).ontask(task)

        #Provide access to the BOI logo
        shutil.copy(
            os.path.join(os.path.dirname(__file__),
                         "header{}.pdf".format(self.year)),
            os.path.join(os.getcwd(), "header.pdf"))

        # Copy translation headers
        copyrecursivelyifnecessary(os.path.join(task.wdir, "..", "general"),
                                   os.path.join(task.wdir, "general"))

        # Register contestheader.tex as \taskheader
        shutil.copy(
            os.path.join(os.path.dirname(__file__), "contestheader.tex"),
            os.path.join(task.wdir, "taskheader.tex"))
        task.supply("latex",
                    def_latex("taskheader", input_latex("taskheader.tex")))

        task.supply("latex", def_latex("olympiadyear", self.year))

        self.mktestcasetable(task)
Exemple #3
0
    def get_contest(self):
        """See docstring in class Loader.

        """
        # Unset stack size limit
        resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY,
                                                   resource.RLIM_INFINITY))

        self.buildpath = os.path.join(self.path, "build")
        # We have to avoid copying the folder contest/build
        # or contest/task/build into contest/build.
        # For this reason, we ignore all files and directories named "build"
        # when copying recursively.
        copyrecursivelyifnecessary((self.path), self.buildpath,
                                   set(["build"]))
        with chdir(self.buildpath):
            rules = ".rules"
            if not os.path.exists(rules):
                os.mkdir(rules)
            rules = os.path.abspath(rules)
            self.contestconfig = ContestConfig(rules,
                                               os.path.basename(self.path))
            self.contestconfig._readconfig("contest-config.py")
            tasknames = [t.name for t in self.contestconfig.tasks]
            usernames = [u.username for u in self.contestconfig.users]
            return self.contestconfig._makecontest(), tasknames, usernames
Exemple #4
0
    def get_contest(self):
        """See docstring in class Loader.

        """
        # Unset stack size limit
        resource.setrlimit(resource.RLIMIT_STACK,
                           (resource.RLIM_INFINITY, resource.RLIM_INFINITY))

        self.buildpath = os.path.join(self.path, "build")
        # We have to avoid copying the folder contest/build
        # or contest/task/build into contest/build.
        # For this reason, we ignore all files and directories named "build"
        # when copying recursively.
        copyrecursivelyifnecessary((self.path), self.buildpath, set(["build"]))
        with chdir(self.buildpath):
            rules = ".rules"
            if not os.path.exists(rules):
                os.mkdir(rules)
            rules = os.path.abspath(rules)
            self.contestconfig = ContestConfig(rules,
                                               os.path.basename(self.path))
            self.contestconfig._readconfig("contest-config.py")
            tasknames = [t.name for t in self.contestconfig.tasks]
            usernames = [u.username for u in self.contestconfig.users]
            return self.contestconfig._makecontest(), tasknames, usernames
Exemple #5
0
    def make(self):
        # Unset stack size limit
        resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY,
                                                   resource.RLIM_INFINITY))

        if not os.path.exists(os.path.join(self.odir, "contest-config.py")):
            raise Exception("Directory doesn't contain contest-config.py")
        self.wdir = os.path.join(self.odir, "build")
        if self.clean:
            shutil.rmtree(self.wdir)
        if not os.path.exists(self.wdir):
            os.mkdir(self.wdir)
        # We have to avoid copying the folder contest/build
        # or contest/task/build into contest/build.
        # For this reason, we ignore all files and directories named "build"
        # when copying recursively.
        copyrecursivelyifnecessary(self.odir, self.wdir, set(["build"]))
        self.wdir = os.path.abspath(self.wdir)
        file_cacher = FileCacher(path=os.path.join(self.wdir, ".cache"))
        try:
            with chdir(self.wdir):
                contestconfig = ContestConfig(
                    os.path.join(self.wdir, ".rules"),
                    os.path.basename(self.odir),
                    ignore_latex=self.no_latex,
                    onlytask=self.task)
                contestconfig._readconfig("contest-config.py")
                if self.task is not None and len(contestconfig.tasks) == 0:
                    raise Exception("Task {} not found".format(self.task))
                cdb = contestconfig._makecontest()
                test_udb = contestconfig._makeuser(
                    contestconfig._mytestuser.username)
                test_gdb = contestconfig._makegroup(
                    contestconfig._mytestuser.group.name, cdb)
                # We're not putting the test user on any team for testing
                # (shouldn't be needed).
                test_pdb = contestconfig._makeparticipation(
                    contestconfig._mytestuser.username, cdb,
                    test_udb, test_gdb, None)
                for t in contestconfig.tasks.values():
                    tdb = t._makedbobject(cdb, file_cacher)
                    t._make_test_submissions(test_pdb, tdb, self.local_test)
        finally:
            file_cacher.destroy_cache()
Exemple #6
0
    def prepare(self):
        # Unset stack size limit
        INFTY = int(.75 * virtual_memory().total)
        resource.setrlimit(resource.RLIMIT_STACK, (INFTY, INFTY))

        self.wdir = os.path.join(self.odir, self.task, "build")

        if self.clean:
            shutil.rmtree(self.wdir)

        if not os.path.exists(self.wdir):
            os.mkdir(self.wdir)

        taskdir = os.path.join(self.odir, self.task)
        wtdir = os.path.join(self.wdir, self.task)

        # We have to avoid copying the folder task/build into itself.
        # For this reason, we ignore all files and directories named "build"
        # when copying recursively.
        copyrecursivelyifnecessary(taskdir, wtdir, set(["build"]))
        self.wdir = os.path.abspath(self.wdir)
Exemple #7
0
    def prepare(self):
        # Unset stack size limit
        resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY,
                                                   resource.RLIM_INFINITY))

        self.wdir = os.path.join(self.odir, self.task, "build")

        if self.clean:
            shutil.rmtree(self.wdir)

        if not os.path.exists(self.wdir):
            os.mkdir(self.wdir)

        taskdir = os.path.join(self.odir, self.task)
        wtdir = os.path.join(self.wdir, self.task)

        # We have to avoid copying the folder task/build into itself.
        # For this reason, we ignore all files and directories named "build"
        # when copying recursively.
        copyrecursivelyifnecessary(taskdir, wtdir, set(["build"]))
        self.wdir = os.path.abspath(self.wdir)
Exemple #8
0
    def make_overview_sheets(self):
        """
        Print overview sheets in two versions (with tasks and all contestants
        of any individual team in one file OR without tasks and all contestants
        separately) and ZIP them together
        """
        if self.contest.ignore_latex:
            return

        teams = {}
        contestants_with_language = {}
        overview_sheets_for = {}

        assert (all(t._feedback_level == FEEDBACK_LEVEL_RESTRICTED
                    for t in self.contest.tasks.values()))
        assert (all(t.score_mode() == SCORE_MODE_MAX_SUBTASK
                    for t in self.contest.tasks.values()))

        prefix = "overview-sheets-for"

        for u in self.contest.users.values():
            team = u.team

            if team not in teams:
                teams[team] = []
            teams[team].append(u)

            for l in u.primary_statements:
                if l not in contestants_with_language:
                    contestants_with_language[l] = []
                contestants_with_language[l].append(u)

        if not os.path.exists("overview"):
            os.mkdir("overview")

        copyrecursivelyifnecessary(
            os.path.join(self.contest.wdir, "general"),
            os.path.join(self.contest.wdir, "overview", "general"))

        self.contest.supply("contestoverview",
                            def_latex("olympiadyear", self.year))

        import csv
        printingunwanted = dict()
        requestsfile = "printingrequested.csv"
        if os.path.exists(requestsfile):
            with open(requestsfile, encoding='utf-8-sig') as f:
                reader = csv.DictReader(f, delimiter=',', quotechar='"')
                for row in reader:
                    c, l = row["Country"], row["Language"]
                    if c not in printingunwanted:
                        printingunwanted[c] = dict()
                    printingunwanted[c]["en"] = row["English"] == "No"
                    printingunwanted[c][l] = row["Own"] == "No"

        with chdir("overview"):
            if not os.path.exists(".overviews-per-language"):
                os.mkdir(".overviews-per-language")

            with chdir(".overviews-per-language"):
                for l, users in contestants_with_language.items():
                    if self.contest.relevant_language and \
                        self.contest.relevant_language != l:
                        continue

                    self.supply_overview()
                    self.contest._build_supplements_for_key("contestoverview")

                    #Provide access to the BOI logo
                    shutil.copy(
                        os.path.join(os.path.dirname(__file__),
                                     "header{}.pdf".format(self.year)),
                        os.path.join(os.getcwd(), "header.pdf"))

                    def do_supply_language():
                        return self.language_supplement(l)

                    def do_supply_credentials():
                        return "\n".join("\\printoverviewpage{%s}{%s, %s}{%s}" % \
                            (u.username, u.lastname, u.firstname, u.password)
                            for u in users)

                    self.contest.supply("lang", do_supply_language)
                    self.contest.supply("credentials", do_supply_credentials)

                    filename = prefix + "-" + l + ".tex"

                    shutil.copy(
                        os.path.join(os.path.dirname(__file__),
                                     "overview-template.tex"), filename)
                    self.contest._build_supplements_for_key("credentials")
                    self.contest._build_supplements_for_key("lang")

                    pdf = PdfFileReader(self.contest.compile(filename))
                    assert (pdf.getNumPages() == len(users))

                    for i, u in enumerate(users):
                        overview_sheets_for[(u.username, l)] = pdf.getPage(i)

                    self.contest.supplements["lang"].parts.clear()
                    self.contest.supplements["credentials"].parts.clear()

            def cleardoublepage(stream):
                if stream.getNumPages() % 2 == 1:
                    stream.addBlankPage()

            for team, users in teams.items():
                if not os.path.exists(team.code):
                    os.mkdir(team.code)

                with chdir(team.code):
                    hw = PdfFileWriter()

                    for u in users:
                        # Overview sheets
                        ow = PdfFileWriter()

                        for l in u.primary_statements:
                            if self.contest.relevant_language and \
                                self.contest.relevant_language != l:
                                continue
                            ow.addPage(overview_sheets_for[(u.username, l)])
                        with open("overview-" + u.username + ".pdf",
                                  "wb") as f:
                            ow.write(f)

                        # handout
                        for l in u.primary_statements:
                            if self.contest.relevant_language and \
                                self.contest.relevant_language != l:
                                continue
                            if printingunwanted \
                                .get(team.name, dict()) \
                                .get(l, False):
                                print_msg("Not adding translation to language "
                                          "{} for user {} to the handout "
                                          "as requested by team {}".format(
                                              l, u.username, team.code))
                                hw.addPage(overview_sheets_for[(u.username,
                                                                l)])
                                cleardoublepage(hw)
                                continue
                            hw.addPage(overview_sheets_for[(u.username, l)])
                            cleardoublepage(hw)

                            for t in sorted(self.contest.tasks.values(),
                                            key=lambda x: x.name):
                                if l in t._statements:
                                    st = PdfFileReader(t._statements[l].file_)
                                    hw.appendPagesFromReader(st)
                                    cleardoublepage(hw)

                    with open("handout.pdf", "wb") as f:
                        hw.write(f)

                job = {
                    "overview-" + u.username + ".pdf":
                    os.path.join(team.code, "overview-" + u.username + ".pdf")
                    for u in users
                }
                job["handout.pdf"] = os.path.join(team.code, "handout.pdf")

                r = ZipRule(os.path.join("..", ".rules"),
                            team.code + "-all.zip", job).ensure()
Exemple #9
0
    def run(self):
        sandbox = LaTeXSandbox(self.file_cacher, name="LaTeX")
        copyrecursivelyifnecessary(self.wdir,
                                   sandbox.get_home_path(),
                                   self.ignore,
                                   self.ignore_ext,
                                   self.do_copy,
                                   mode=0o777)

        stored_cache_path = os.path.join(config.latex_cache_dir, self.party,
                                         config.latex_distro)
        sandbox_cache_path = os.path.join(sandbox.get_home_path(),
                                          config.latex_distro)
        # Create the LaTeX cache directory if it doesn't exist
        os.makedirs(stored_cache_path, exist_ok=True)
        # Copy the LaTeX cache directory into the sandbox
        copyrecursivelyifnecessary(stored_cache_path,
                                   sandbox_cache_path,
                                   mode=0o777)

        sandbox.allow_writing_all()

        relpath = os.path.relpath(os.getcwd(), self.wdir)
        sandbox.chdir = os.path.join(sandbox.chdir, relpath)

        depsfile = os.path.join(sandbox.get_home_path(), relpath, ".deps")

        # Delete the dependencies file before running latex to ensure
        # that we don't read a left-over dependencies file in the end
        # (if latex fails to write the dependencies file).
        deletefile(depsfile)

        success = sandbox.execute_without_std(self.command, wait=True)

        # Don't save the result if the sandbox or LaTeX failed.
        # (We don't reliably detect when a previously missing file was added,
        # for example when a missing package was installed.)
        if not success or sandbox.failed():
            self.result.badfail = True

        self.result.log['code'] = sandbox.failed()
        self.result.log['out'] = sandbox.get_stdout()
        self.result.log['err'] = sandbox.get_stderr()

        self.result.add_dependency(self.source)

        if self.result.log['code']:
            self.result.log['err'] += "\n\n" + ("#" * 40) + "\n" + \
                "SANDBOX: " + sandbox.get_root_path() + "\n" + \
                "MESSAGE: " + sandbox.get_human_exit_description() + "\n" + \
                "LOG FILE:\n" + sandbox.get_log_file_contents()
        else:
            copyifnecessary(
                os.path.join(sandbox.get_home_path(), relpath, self.output),
                os.path.join(self.wdir, relpath, self.output))
            self.result.add_output(self.output)

            #Copy potentially changed latex cache
            copyrecursivelyifnecessary(sandbox_cache_path, stored_cache_path)

            # Change to the same working directory that lualatex used (which is
            # inside the sandbox).
            with chdir(os.path.join(sandbox.get_home_path(), relpath)):
                readmakefile(".deps", self.result, True)

            def convert(path):
                if path.startswith(os.path.join(config.latex_distro, "")):
                    return os.path.join(config.latex_cache_dir, self.party,
                                        path)
                else:
                    return path

            self.result.dependencies = \
               {convert(path): hash
                for path, hash in self.result.dependencies.items()}

            sandbox.cleanup(not config.keep_sandbox)
Exemple #10
0
    def make_helper(self):
        # Unset stack size limit
        INFTY = int(.75 * virtual_memory().total)
        resource.setrlimit(resource.RLIMIT_STACK, (INFTY, INFTY))

        if not os.path.exists(os.path.join(self.odir, "contest-config.py")):
            raise Exception("Directory doesn't contain contest-config.py")
        self.wdir = os.path.join(self.odir, "build")
        if self.clean:
            shutil.rmtree(self.wdir)
        if not os.path.exists(self.wdir):
            os.mkdir(self.wdir)
        # We have to avoid copying the folder contest/build
        # or contest/task/build into contest/build.
        # For this reason, we ignore all files and directories named "build"
        # when copying recursively.
        copyrecursivelyifnecessary(self.odir, self.wdir, set(["build"]))

        self.wdir = os.path.abspath(self.wdir)
        with chdir(self.wdir):
            contestconfig = ContestConfig(os.path.join(self.wdir, ".rules"),
                                          os.path.basename(self.odir),
                                          safe_latex=self.safe_latex)

            # Read the configuration file and build.
            contestconfig._readconfig("contest-config.py")

            with SessionGen() as session:

                def session_add(k, v):
                    session.add(v)

                # Variables like udbs, teamdbs, cdb, ... contain objects before
                # they've been put into the database.
                # Their counterpars udb1s, teamdb1s, cdb1, ... contain the
                # objects that are actually in the database (which are copies
                # of the objects in udbs, ...).

                # Create users in the database.
                udbs = [
                    contestconfig._makeuser(u) for u in contestconfig.users
                ]
                udb1s = _update_list_with_key(session.query(User).all(),
                                              udbs,
                                              lambda u: u.username,
                                              preserve_old=True,
                                              update_value_fn=update_user,
                                              creator_fn=session_add)
                udbs = {u.username: u for u in udbs}

                # Create teams in the database.
                teamdbs = [
                    contestconfig._maketeam(t) for t in contestconfig.teams
                ]
                teamdb1s = _update_list_with_key(session.query(Team).all(),
                                                 teamdbs,
                                                 lambda t: t.code,
                                                 preserve_old=True,
                                                 update_value_fn=update_team,
                                                 creator_fn=session_add)
                teamdbs = {t.code: t for t in teamdbs}

                # Create contest (including associated user groups) in the database.
                cdb = contestconfig._makecontest()
                cdbs = [cdb]
                cdb1s = _update_list_with_key(session.query(Contest).all(),
                                              cdbs,
                                              lambda c: c.name,
                                              preserve_old=True,
                                              update_value_fn=update_contest,
                                              creator_fn=session_add)
                cdb1 = cdb1s[cdb.name]

                # Set the contest's main group.
                cdb1.main_group = cdb1.get_group(
                    contestconfig.defaultgroup.name)

                # Create participations in the database.

                # Team object for a given user
                def user_team(u):
                    t = contestconfig.users[u].team
                    if t is None:
                        return None
                    else:
                        return teamdbs[t.code]

                # Team object in the database for a given user
                def user_team1(u):
                    t = contestconfig.users[u].team
                    if t is None:
                        return None
                    else:
                        return teamdb1s[t.code]

                def make_participation(u):
                    gdb = cdb.get_group(contestconfig.users[u].group.name)
                    return contestconfig._makeparticipation(
                        u, cdb, udbs[u], gdb, user_team(u))

                pdbs = [make_participation(u) for u in contestconfig.users]
                pdb1s = _update_list_with_key(cdb1.participations,
                                              pdbs,
                                              lambda p : p.user.username,
                                              preserve_old= \
                                                  self.preserve_participations,
                                              update_value_fn=update_participation)
                pdbs = {p.user.username: p for p in pdbs}

                for username, u in iteritems(pdb1s):
                    if username in contestconfig.users:
                        u.user = udb1s[username]
                        u.group = cdb1.get_group(
                            contestconfig.users[username].group.name)
                        u.team = user_team1(username)

                # The test user participation.
                test_pdb = pdbs[contestconfig._mytestuser.username]
                test_pdb1 = pdb1s[contestconfig._mytestuser.username]

                # This is an ugly hack to prevent problems when reordering or
                # adding tasks. Since we delete after adding and updating,
                # there might otherwise at one point be two tasks with the same
                # number.
                for t in cdb1.tasks:
                    t.num += len(contestconfig.tasks) + len(cdb1.tasks)

                tdbs = [
                    t._makedbobject(cdb, self.file_cacher)
                    for t in contestconfig.tasks.values()
                ]
                tdbs_dict = {t.name: t for t in tdbs}

                # We only set the active dataset when importing a new task.
                # Afterwards, the active dataset has to be set using the web
                # interface.

                def task_creator(name, v):
                    tdb = tdbs_dict[name]
                    ddb1 = session.query(Dataset) \
                        .filter(Dataset.task == v) \
                        .filter(Dataset.description ==
                                tdb.active_dataset.description).first()
                    assert ddb1 is not None
                    v.active_dataset = ddb1

                tdb1s = _update_list_with_key(cdb1.tasks,
                                              tdbs,
                                              lambda t: t.name,
                                              preserve_old=False,
                                              update_value_fn=update_task,
                                              creator_fn=task_creator)
                tdbs = {t.name: t for t in tdbs}

                if not self.no_test:
                    ans = input("Replace (perhaps not yet existent) test "
                        "submissions? (This will cause workers to crash if "
                        "there are still test submissions pending!) [y/N] ") \
                            .strip().lower()
                    if ans not in ["y", "yes"]:
                        self.no_test = True

                sdb1ss = {}
                if not self.no_test:
                    logger.warning("Replacing test submissions")
                    for t in contestconfig.tasks:
                        tdb = tdbs[t]
                        tdb1 = tdb1s[t]
                        # Mark old test submissions for deletion.
                        sdb1s = session.query(Submission) \
                            .filter(Submission.participation == test_pdb1) \
                            .filter(Submission.task == tdb1) \
                            .filter(Submission.additional_info != None).all()
                        for sdb1 in sdb1s:
                            assert sdb1.is_unit_test()
                            _to_delete.append(sdb1)

                        # Create test submissions in the database.
                        sdbs = contestconfig.tasks[t]._make_test_submissions(
                            test_pdb, tdb, False)
                        sdb1s = []
                        for sdb in sdbs:
                            sdb1 = _copy(sdb)
                            sdb1.task = tdb1
                            sdb1.participation = test_pdb1
                            session.add(sdb1)
                            sdb1s.append(sdb1)
                        sdb1ss[t] = sdb1s

                for v in _to_delete:
                    if isinstance(v, Task):
                        logger.warning("Removing task {}".format(v.name))
                        if any(not s.is_unit_test() for s in v.submissions):
                            logger.warning(
                                "There are submissions for task {}.".format(
                                    v.name))
                            if not self.force:
                                logger.error("Aborting. Run with -f to force "
                                             "deletion.")
                                return
                    elif isinstance(v, Participation):
                        logger.warning("Removing participation {}".format(
                            v.user.username))
                        if any(not s.is_unit_test() for s in v.submissions):
                            logger.warning(
                                "There are submissions for participation {}.".
                                format(v.user.username))
                            if not self.force:
                                logger.error("Aborting. Run with -f to force "
                                             "deletion.")
                                return

                if self.force:
                    logger.warning("Force flace -f set.")

                ans = input("Proceed? [y/N] ").strip().lower()
                if ans not in ["y", "yes"]:
                    logger.error("Aborting.")
                    return

                # Delete marked objects
                for v in _to_delete:
                    # Participation objects don't have to be explicitly
                    # deleted, they are a part of cdb / cdb1.
                    # TODO We might want to move the handling of
                    # participations to ContestConfig + importing as
                    # for groups.
                    if not isinstance(v, Participation):
                        session.delete(v)

                session.commit()

                if not self.no_test:
                    evaluation_service = self.connect_to(
                        ServiceCoord("EvaluationService", 0))
                    for t in contestconfig.tasks:
                        sdb1s = sdb1ss[t]
                        # Notify EvaluationService of the new test submissions.
                        for sdb1 in sdb1s:
                            evaluation_service.new_submission(
                                submission_id=sdb1.id)
                    evaluation_service.disconnect()

                logger.info("Import finished (new contest id: %s).",
                            cdb.id if cdb1 is None else cdb1.id)

        contestconfig.finish()
Exemple #11
0
    def make_helper(self):
        # Unset stack size limit
        resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY,
                                                   resource.RLIM_INFINITY))

        if not os.path.exists(os.path.join(self.odir, "contest-config.py")):
            raise Exception("Directory doesn't contain contest-config.py")
        self.wdir = os.path.join(self.odir, "build")
        if self.clean:
            shutil.rmtree(self.wdir)
        if not os.path.exists(self.wdir):
            os.mkdir(self.wdir)
        # We have to avoid copying the folder contest/build
        # or contest/task/build into contest/build.
        # For this reason, we ignore all files and directories named "build"
        # when copying recursively.
        copyrecursivelyifnecessary(self.odir, self.wdir, set(["build"]))

        self.wdir = os.path.abspath(self.wdir)
        with chdir(self.wdir):
            contestconfig = ContestConfig(
                os.path.join(self.wdir, ".rules"),
                os.path.basename(self.odir))

            # Read the configuration file and build.
            contestconfig._readconfig("contest-config.py")

            with SessionGen() as session:
                def session_add(k, v):
                    session.add(v)

                # Variables like udbs, teamdbs, cdb, ... contain objects before
                # they've been put into the database.
                # Their counterpars udb1s, teamdb1s, cdb1, ... contain the
                # objects that are actually in the database (which are copies
                # of the objects in udbs, ...).

                # Create users in the database.
                udbs = [contestconfig._makeuser(u) for u in contestconfig.users]
                udb1s = _update_list_with_key(session.query(User).all(),
                                              udbs,
                                              lambda u : u.username,
                                              preserve_old=True,
                                              update_value_fn=update_user,
                                              creator_fn=session_add)
                udbs = {u.username : u for u in udbs}

                # Create teams in the database.
                teamdbs = [contestconfig._maketeam(t) for t in contestconfig.teams]
                teamdb1s = _update_list_with_key(session.query(Team).all(),
                                                 teamdbs,
                                                 lambda t : t.code,
                                                 preserve_old=True,
                                                 update_value_fn=update_team,
                                                 creator_fn=session_add)
                teamdbs = {t.code : t for t in teamdbs}

                # Create contest (including associated user groups) in the database.
                cdb = contestconfig._makecontest()
                cdbs = [cdb]
                cdb1s = _update_list_with_key(session.query(Contest).all(),
                                              cdbs,
                                              lambda c : c.name,
                                              preserve_old=True,
                                              update_value_fn=update_contest,
                                              creator_fn=session_add)
                cdb1 = cdb1s[cdb.name]

                # Set the contest's main group.
                cdb1.main_group = cdb1.get_group(contestconfig.defaultgroup.name)

                # Create participations in the database.

                # Team object for a given user
                def user_team(u):
                    t = contestconfig.users[u].team
                    if t is None:
                        return None
                    else:
                        return teamdbs[t.code]
                # Team object in the database for a given user
                def user_team1(u):
                    t = contestconfig.users[u].team
                    if t is None:
                        return None
                    else:
                        return teamdb1s[t.code]

                def make_participation(u):
                    gdb = cdb.get_group(contestconfig.users[u].group.name)
                    return contestconfig._makeparticipation(u, cdb, udbs[u],
                                                            gdb, user_team(u))

                pdbs = [make_participation(u) for u in contestconfig.users]
                pdb1s = _update_list_with_key(cdb1.participations,
                                              pdbs,
                                              lambda p : p.user.username,
                                              preserve_old=True,
                                              update_value_fn=update_participation)
                pdbs = {p.user.username : p for p in pdbs}

                for username, u in iteritems(pdb1s):
                    u.user = udb1s[username]
                    u.group = cdb1.get_group(
                        contestconfig.users[username].group.name)
                    u.team = user_team1(username)

                # The test user participation.
                test_pdb = pdbs[contestconfig._mytestuser.username]
                test_pdb1 = pdb1s[contestconfig._mytestuser.username]

                # This is an ugly hack to prevent problems when reordering or
                # adding tasks. Since we delete after adding and updating,
                # there might otherwise at one point be two tasks with the same
                # number.
                for t in cdb1.tasks:
                    t.num += len(contestconfig.tasks) + len(cdb1.tasks)

                tdbs = [t._makedbobject(cdb, self.file_cacher)
                        for t in contestconfig.tasks.values()]
                tdbs_dict = {t.name: t for t in tdbs}
                # We only set the active dataset when importing a new task.
                # Afterwards, the active dataset has to be set using the web
                # interface.

                def task_creator(name, v):
                    tdb = tdbs_dict[name]
                    ddb1 = session.query(Dataset) \
                        .filter(Dataset.task == v) \
                        .filter(Dataset.description ==
                                tdb.active_dataset.description).first()
                    assert ddb1 is not None
                    v.active_dataset = ddb1
                tdb1s = _update_list_with_key(cdb1.tasks,
                                              tdbs,
                                              lambda t : t.name,
                                              preserve_old=False,
                                              update_value_fn=update_task,
                                              creator_fn=task_creator)
                tdbs = {t.name : t for t in tdbs}

                sdb1ss = {}
                if not self.no_test:
                    logger.warning("Replacing test submissions")
                    for t in contestconfig.tasks:
                        tdb = tdbs[t]
                        tdb1 = tdb1s[t]
                        # Mark old test submissions for deletion.
                        sdb1s = session.query(Submission) \
                            .filter(Submission.participation == test_pdb1) \
                            .filter(Submission.task == tdb1) \
                            .filter(Submission.additional_info != None).all()
                        for sdb1 in sdb1s:
                            assert sdb1.is_unit_test()
                            _to_delete.append(sdb1)

                        # Create test submissions in the database.
                        sdbs = contestconfig.tasks[t]._make_test_submissions(
                            test_pdb, tdb, False)
                        sdb1s = []
                        for sdb in sdbs:
                            sdb1 = _copy(sdb)
                            sdb1.task = tdb1
                            sdb1.participation = test_pdb1
                            session.add(sdb1)
                            sdb1s.append(sdb1)
                        sdb1ss[t] = sdb1s

                for v in _to_delete:
                    if isinstance(v, Task):
                        logger.warning("Removing task {}"
                                       .format(v.name))
                        if any(not s.is_unit_test() for s in v.submissions):
                            logger.warning(
                                "There are submissions for task {}."
                                .format(v.name))
                            if not self.force:
                                logger.error("Aborting. Run with -f to force "
                                             "deletion.")
                                return
                    elif isinstance(v, Participation):
                        logger.warning("Removing participation {}"
                                       .format(v.user.username))
                        if any(not s.is_unit_test() for s in v.submissions):
                            logger.warning(
                                "There are submissions for participation {}."
                                .format(v.user.username))
                            if not self.force:
                                logger.error("Aborting. Run with -f to force "
                                             "deletion.")
                                return

                if self.force:
                    logger.warning("Force flace -f set.")

                ans = input("Proceed? [y/N] ").strip().lower()
                if ans not in ["y", "yes"]:
                    logger.error("Aborting.")
                    return

                # Delete marked objects
                for v in _to_delete:
                    session.delete(v)

                session.commit()

                if not self.no_test:
                    evaluation_service = self.connect_to(
                        ServiceCoord("EvaluationService", 0))
                    for t in contestconfig.tasks:
                        sdb1s = sdb1ss[t]
                        # Notify EvaluationService of the new test submissions.
                        for sdb1 in sdb1s:
                            evaluation_service.new_submission(
                                submission_id=sdb1.id)
                    evaluation_service.disconnect()

                logger.info("Import finished (new contest id: %s).",
                            cdb.id if cdb1 is None else cdb1.id)