def _readconfig(self, filename): if not self.minimal: print_msg("Loading contest {}".format(self.contestname), headerdepth=1) super(ContestConfig, self)._readconfig(filename) self._initialize_ranking()
def user(self, username, password, firstname, lastname, group=None, ip=None, hidden=False, unrestricted=False, timezone=None, primary_statements=[], team=None): """ Add a user participating in this contest. username (unicode): user name (for login) password (unicode): password used both for the user and for the participation firstname (unicode): first name lastname (unicode): last name group (MyGroup): the group to add this user to (by default the default group, which is usually called main) ip (unicode): ip address the user must log in from (if this feature is enabled in CMS) hidden (bool): whether this user is hidden (not shown on the official scoreboard) unrestricted (bool): whether this user is unrestricted (can submit at any time) timezone (unicode): time zone for this user (if different from contest time zone) primary_statements (string[] or {string: string[]}): either a list of languages (it is assumed that all tasks have a translation for this language) or a dictionary mapping task names to language names team (string or MyTeam): (name of) the team the user belongs to return (MyUser): object representing the created user """ if self.minimal: return if username in self.users: raise Exception( "User {} specified multiple times".format(username)) if group is None: if self.defaultgroup is None: raise Exception("You have to specify a group") group = self.defaultgroup if team is not None and not isinstance(team, MyTeam): team = self.teams[team] print_msg("Adding user {} to group {}".format(username, group.name), headerdepth=10) self.users[username] = user = \ MyUser(username, password, group, firstname, lastname, ip, hidden, unrestricted, timezone, primary_statements, team) return user
def timezone(self, s): """ Set the contest time zone. s (unicode): contest time zone (e.g. Europe/Berlin) """ if not self.minimal: print_msg("Setting timezone to {}".format(s), headerdepth=10) self._timezone = s
def description(self, s): """ Set the contest description. s (unicode): contest description """ if not self.minimal: print_msg("Setting description to {}".format(s), headerdepth=10) self._description = s
def user_group(self, s, start, stop, analysis_start=None, analysis_stop=None, per_user_time=None): """ Create a user group. s (unicode): user group name; if the group is called "main", it is assumed to be the default group. start (string): start time (should be created via a call to time()) stop (string): stop time (should be created via a call to time()) analysis_start (string): enable analysis with this start time (should be created via a call to time()) analysis_stop (string): enable analysis with this stop time (should be created via a call to time()) per_user_time (timedelta): enable USACO mode with this time per user; see :ref:`configuringacontest_usaco-like-contests` return (MyGroup): object representing the created group """ if s in self.groups: raise Exception("Group {} specified multiple times".format(s)) if self.minimal: return if (analysis_start is None) ^ (analysis_stop is None): raise Exception("Analysis start and stop time can only be used together") usaco_mode_str = "" if per_user_time is not None: usaco_mode_str = \ " in USACO mode with time {}".format(per_user_time) analysis_mode_str = "" if analysis_start is not None: analysis_mode_str = \ ", analysing from {} to {}".format(analysis_start,analysis_stop) print_msg("Creating user group {} (working from {} to {}{}{})" .format(s, start, stop, usaco_mode_str, analysis_mode_str), headerdepth=10) # We allow specifying an integer or a timedelta try: per_user_time = per_user_time.seconds except AttributeError: pass self.groups[s] = r = MyGroup(s, start, stop, analysis_start, analysis_stop, per_user_time) if s == "main": self.defaultgroup = r return r
def supplement_file(self, key, filename): """ Set the storage file for supplements registered with the given file type. key (string): a string specifying for which purpose to use the supplement filename (string): name of the file to save the supplements to """ print_msg("Supplement file for {} set to {}".format(key, filename), headerdepth=10) self.supplements[key].add_file(filename)
def _task(self, s, feedback, score_mode, minimal, standalone_task=False): """ Add a task to this contest (full version, not accessible from config.py). s (unicode): task name; the task description has to reside in the folder with the same name feedback: type of feedback (one of the variables no_feedback, partial_feedback, full_feedback, restricted_feedback) score_mode: how to calculate the final score (one of SCORE_MODE_MAX, SCORE_MODE_MAX_SUBTASK, SCORE_MODE_MAX_TOKENED_LAST) minimal (bool): only try to compile statement? """ # Check if this task should be ignored if self.onlytask is not None and self.onlytask != s: return if not os.path.isdir(s): raise Exception("No directory found for task {}".format(s)) if s in self.tasks: raise Exception("Task {} specified multiple times".format(s)) with chdir(s): if not os.path.isfile("config.py"): raise Exception("Couldn't find task config file. Make sure it " "is named 'config.py' and located on the " "topmost level of the folder {}" .format(os.getcwd())) with TaskConfig(self, os.path.abspath(".rules"), s, len(self.tasks), feedback, score_mode, ignore_latex=self.ignore_latex, relevant_language=self.relevant_language, minimal=minimal, standalone_task=standalone_task) as taskconfig: for f in self.ontasks: f(taskconfig) taskconfig._readconfig("config.py") taskconfig._printresult() self.tasks[s] = taskconfig if minimal: print_msg("Statement for task {} generated successfully". format(s), success=True) else: print_msg("Task {} loaded completely".format(s), success=True)
def user_group(self, s, start, stop, analysis_start=None, analysis_stop=None, per_user_time=None): """ Create a user group. s (unicode): user group name; if the group is called "main", it is assumed to be the default group. start (string): start time (should be created via a call to time()) stop (string): stop time (should be created via a call to time()) analysis_start (string): enable analysis with this start time (should be created via a call to time()) analysis_stop (string): enable analysis with this stop time (should be created via a call to time()) per_user_time (timedelta): enable USACO mode with this time per user; see :ref:`configuringacontest_usaco-like-contests` return (MyGroup): object representing the created group """ if s in self.groups: raise Exception("Group {} specified multiple times".format(s)) if (analysis_start is None) ^ (analysis_stop is None): raise Exception("Analysis start and stop time can only be used together") usaco_mode_str = "" if per_user_time is not None: usaco_mode_str = \ " in USACO mode with time {}".format(per_user_time) analysis_mode_str = "" if analysis_start is not None: analysis_mode_str = \ ", analysing from {} to {}".format(analysis_start,analysis_stop) print_msg("Creating user group {} (working from {} to {}{}{})" .format(s, start, stop, usaco_mode_str, analysis_mode_str), headerdepth=10) # We allow specifying an integer or a timedelta try: per_user_time = per_user_time.seconds except AttributeError: pass self.groups[s] = r = MyGroup(s, start, stop, analysis_start, analysis_stop, per_user_time) if s == "main": self.defaultgroup = r return r
def _task(self, s, feedback, minimal): """ Add a task to this contest (full version, not accessible from config.py). s (unicode): task name; the task description has to reside in the folder with the same name feedback: type of feedback (one of the variables no_feedback, partial_feedback, full_feedback, restricted_feedback) minimal (bool): only try to compile statement? """ # Check if this task should be ignored if self.onlytask is not None and self.onlytask != s: return if not os.path.isdir(s): raise Exception("No directory found for task {}".format(s)) if s in self.tasks: raise Exception("Task {} specified multiple times".format(s)) with chdir(s): if not os.path.isfile("config.py"): raise Exception("Couldn't find task config file. Make sure it " "is named 'config.py' and located on the " "topmost level of the folder {}" .format(os.getcwd())) with TaskConfig(self, os.path.abspath(".rules"), s, len(self.tasks), feedback, ignore_latex=self.ignore_latex, minimal=minimal) as taskconfig: for f in self.ontasks: f(taskconfig) taskconfig._readconfig("config.py") taskconfig._printresult() self.tasks[s] = taskconfig if minimal: print_msg("Statement for task {} generated successfully". format(s), success=True) else: print_msg("Task {} loaded completely".format(s), success=True)
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()
def user(self, username, password, firstname, lastname, group=None, ip=None, hidden=False, unofficial=False, unrestricted=False, timezone=None, primary_statements=None, team=None): """ Add a user participating in this contest. username (unicode): user name (for login) password (unicode): password used both for the user and for the participation firstname (unicode): first name lastname (unicode): last name group (MyGroup): the group to add this user to (by default the default group, which is usually called main) ip (unicode): ip address the user must log in from (if this feature is enabled in CMS) hidden (bool): whether this user is hidden (not shown on the official scoreboard) unofficial (bool): whether this user is unofficial (can be hidden on the official scoreboard) unrestricted (bool): whether this user is unrestricted (can submit at any time) timezone (unicode): time zone for this user (if different from contest time zone) primary_statements (string[] or None): the list of standard task languages for this user (if None, the languages for the corresponding team will be used) team (string or MyTeam): (name of) the team the user belongs to return (MyUser): object representing the created user """ team = team or self._current_team if not isinstance(team, MyTeam): team = self.teams[team] primary_statements = primary_statements or team.primary_statements if username in self.users: raise Exception( "User {} specified multiple times".format(username)) if group is None: if self.defaultgroup is None: raise Exception("You have to specify a group") group = self.defaultgroup print_msg("Adding user {} to group {}".format(username, group.name), headerdepth=10) self.users[username] = user = \ MyUser(username, password, group, firstname, lastname, ip, hidden, unofficial, unrestricted, timezone, primary_statements[:], team) return user