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()
def build(self): file_cacher = FileCacher(path=os.path.join(self.wdir, ".cache")) try: with chdir(self.wdir): contestconfig = \ ContestConfig(os.path.join(self.wdir, ".rules"), "hidden contest", minimal=self.minimal) copyifnecessary(os.path.join(contestconfig._get_ready_dir(), "contest-template.py"), os.path.join(self.wdir, "c.py")) contestconfig._readconfig("c.py") contestconfig._task( self.task, contestconfig.full_feedback, self.minimal) if not self.minimal: 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() primary_statements = [s for s in list(list(contestconfig.tasks.values())[ 0]._statements.values()) if s.primary] if len(primary_statements) == 0: return None elif len(primary_statements) == 1: return os.path.abspath(primary_statements[0].file_) else: raise Exception("More than one primary statement")
class GerImport(Service): def __init__(self, odir, no_test, clean, force): self.odir = odir self.no_test = no_test self.clean = clean self.force = force Service.__init__(self) def make(self): self.file_cacher = FileCacher() try: self.make_helper() finally: self.file_cacher.destroy_cache() 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)