def __init__(self, import_source, cws_address, no_import=False, start_from=0): self.import_source = import_source self.cws_address = cws_address self.no_import = no_import self.start_from = start_from self.start = None self.speed = 1 self.speed_lock = RLock() self.events = [] self.importer = ContestImporter(drop=False, import_source=import_source, only_files=False, no_files=False, no_submissions=True)
def __init__(self, import_source, cws_address, no_import=False, start_from=0): self.import_source = import_source self.cws_address = cws_address self.no_import = no_import self.start_from = start_from self.start = None self.speed = 1 self.speed_lock = RLock() self.events = [] self.importer = ContestImporter( drop=False, import_source=import_source, only_files=False, no_files=False, no_submissions=True )
class ContestReplayer(object): def __init__(self, import_source, cws_address, no_import=False, start_from=0): self.import_source = import_source self.cws_address = cws_address self.no_import = no_import self.start_from = start_from self.start = None self.speed = 1 self.speed_lock = RLock() self.events = [] self.importer = ContestImporter(drop=False, import_source=import_source, only_files=False, no_files=False, no_submissions=True) def run(self): """Main routine for replaying a contest, handling arguments from command line, and managing the speed of the replayer. """ if not self.no_import: logger.info("Importing contest...") self.importer.run() logger.info("Contest imported.") logger.info("Please run CMS against the contest (with ip_lock=False).") logger.info("Please ensure that:") logger.info("- the contest is active (we are between start and stop);") logger.info("- the minimum interval for submissions and usertests ") logger.info(" (contest- and task-wise) is None.") logger.info("Then press enter to start.") raw_input() with io.open(os.path.join(self.import_source, "contest.json"), "rt", encoding="utf-8") as fin: self.compute_events(json.load(fin)) thread = Thread(target=self.replay) thread.daemon = True thread.start() logger.info("Loading submission data...") while self.start is None: time.sleep(1) while thread.isAlive(): new_speed = raw_input("Write the speed multiplier or q to quit " "(time %s, multiplier %s):\n" % (to_time((time.time() - self.start) * self.speed), self.speed)) if new_speed == "q": return 0 elif new_speed != "": try: new_speed = int(new_speed) except ValueError: logger.warning("Speed multiplier could not be parsed.") else: self.recompute_start(new_speed) return 0 def compute_events(self, contest): tasks = dict((task["name"], task["num"]) for task in contest["tasks"]) for user in contest["users"]: tasks_num = dict((task["name"], 1) for task in contest["tasks"]) for submission in sorted(user["submissions"], key=lambda x: x["timestamp"]): num = tasks_num[submission["task"]] tasks_num[submission["task"]] += 1 self.events.append([ submission["timestamp"] - contest["start"], user["username"], user["password"], tasks[submission["task"]], submission["task"], "s", # For submit events. (submission["files"], submission["language"]), ]) if submission["token"] is not None: self.events.append([ submission["token"]["timestamp"] - contest["start"], user["username"], user["password"], tasks[submission["task"]], submission["task"], "t", # For token events. num, ]) # TODO: add user test events. self.events.sort() def recompute_start(self, new_speed): """Utility to recompute the start time of a contest passing from a speed of self.speed to a speed of new_speed. new_speed(int): the new speed for the contest replayer. """ with self.speed_lock: if self.speed != new_speed: self.start = self.start \ + (time.time() - self.start) * (new_speed - self.speed) \ * 1.0 / new_speed self.speed = new_speed def submit(self, timestamp, username, password, t_id, t_short, files, language): """Execute the request for a submission. timestamp (int): seconds from the start. username (string): username issuing the submission. password (string): password of username. t_id (string): id of the task. t_short (string): short name of the task. files ([dict]): list of dictionaries with keys 'filename' and 'digest'. language (string): the extension the files should have. """ logger.info("%s - Submitting for %s on task %s." % (to_time(timestamp), username, t_short)) if len(files) != 1: logger.error("We cannot submit more than one file.") return # Copying submission files into a temporary directory with the # correct name. Otherwise, SubmissionRequest does not know how # to interpret the file (and which language are they in). temp_dir = tempfile.mkdtemp(dir=config.temp_dir) for file_ in files: temp_filename = os.path.join(temp_dir, file_["filename"].replace("%l", language)) shutil.copy( os.path.join(self.import_source, "files", files[0]["digest"]), temp_filename ) file_["filename"] = temp_filename filename = os.path.join(files[0]["filename"]) browser = Browser() browser.set_handle_robots(False) step(LoginRequest(browser, username, password, base_url=self.cws_address)) step(SubmitRequest(browser, (int(t_id), t_short), filename=filename, base_url=self.cws_address)) shutil.rmtree(temp_dir) def token(self, timestamp, username, password, t_id, t_short, submission_num): """Execute the request for releasing test a submission. timestamp (int): seconds from the start. username (string): username issuing the submission. password (string): password of username. t_id (string): id of the task. t_short (string): short name of the task. submission_num (string): id of the submission to release test. """ logger.info("%s - Playing token for %s on task %s" % (to_time(timestamp), username, t_short)) browser = Browser() browser.set_handle_robots(False) step(LoginRequest(browser, username, password, base_url=self.cws_address)) step(TokenRequest(browser, (int(t_id), t_short), submission_num=submission_num, base_url=self.cws_address)) def replay(self): """Start replaying the events in source on the CWS at the specified address. """ with self.speed_lock: index = 0 if self.start_from is not None: while index < len(self.events) \ and float(self.events[index][0]) < self.start_from: index += 1 self.start = time.time() - self.start_from else: self.start = time.time() while index < len(self.events): timestamp, username, password, task_id, task_name, type_, data \ = self.events[index] to_wait = (timestamp / self.speed - (time.time() - self.start)) while to_wait > .5: if 0 < to_wait % 10 <= .5: logger.info("Next event in %d seconds." % int(to_wait)) time.sleep(.5) to_wait = (timestamp / self.speed - (time.time() - self.start)) if to_wait > 0: time.sleep(to_wait) if type_ == "s": # Submit. files, language = data self.submit(timestamp=timestamp, username=username, password=password, t_id=task_id, t_short=task_name, files=files, language=language) elif type_ == "t": # Token. self.token(timestamp=timestamp, username=username, password=password, t_id=task_id, t_short=task_name, submission_num=data) else: logger.warning("Unexpected type `%s', ignoring." % type_) index += 1
class ContestReplayer(object): def __init__(self, import_source, cws_address, no_import=False, start_from=0): self.import_source = import_source self.cws_address = cws_address self.no_import = no_import self.start_from = start_from self.start = None self.speed = 1 self.speed_lock = RLock() self.events = [] self.importer = ContestImporter(drop=False, import_source=import_source, only_files=False, no_files=False, no_submissions=True) def run(self): """Main routine for replaying a contest, handling arguments from command line, and managing the speed of the replayer. """ if not self.no_import: logger.info("Importing contest...") self.importer.run() logger.info("Contest imported.") logger.info("Please run CMS against the contest (with ip_lock=False).") logger.info("Please ensure that:") logger.info("- the contest is active (we are between start and stop);") logger.info("- the minimum interval for submissions and usertests ") logger.info(" (contest- and task-wise) is None.") logger.info("Then press enter to start.") raw_input() with open(os.path.join(self.import_source, "contest.json")) as fin: self.compute_events(json.load(fin)) thread = Thread(target=self.replay) thread.daemon = True thread.start() logger.info("Loading submission data...") while self.start is None: time.sleep(1) while thread.isAlive(): new_speed = raw_input( "Write the speed multiplier or q to quit " "(time %s, multiplier %s):\n" % (to_time( (time.time() - self.start) * self.speed), self.speed)) if new_speed == "q": return 0 elif new_speed != "": try: new_speed = int(new_speed) except ValueError: logger.warning("Speed multiplier could not be parsed.") else: self.recompute_start(new_speed) return 0 def compute_events(self, contest): tasks = dict((task["name"], task["num"]) for task in contest["tasks"]) for user in contest["users"]: tasks_num = dict((task["name"], 1) for task in contest["tasks"]) for submission in sorted(user["submissions"], key=lambda x: x["timestamp"]): num = tasks_num[submission["task"]] tasks_num[submission["task"]] += 1 self.events.append([ submission["timestamp"] - contest["start"], user["username"], user["password"], tasks[submission["task"]], submission["task"], "s", # For submit events. (submission["files"], submission["language"]), ]) if submission["token"] is not None: self.events.append([ submission["token"]["timestamp"] - contest["start"], user["username"], user["password"], tasks[submission["task"]], submission["task"], "t", # For token events. num, ]) # TODO: add user test events. self.events.sort() def recompute_start(self, new_speed): """Utility to recompute the start time of a contest passing from a speed of self.speed to a speed of new_speed. new_speed(int): the new speed for the contest replayer. """ with self.speed_lock: if self.speed != new_speed: self.start = self.start \ + (time.time() - self.start) * (new_speed - self.speed) \ * 1.0 / new_speed self.speed = new_speed def submit(self, timestamp, username, password, t_id, t_short, files, language): """Execute the request for a submission. timestamp (int): seconds from the start. username (string): username issuing the submission. password (string): password of username. t_id (string): id of the task. t_short (string): short name of the task. files ([dict]): list of dictionaries with keys 'filename' and 'digest'. language (string): the extension the files should have. """ logger.info("%s - Submitting for %s on task %s." % (to_time(timestamp), username, t_short)) if len(files) != 1: logger.error("We cannot submit more than one file.") return # Copying submission files into a temporary directory with the # correct name. Otherwise, SubmissionRequest does not know how # to interpret the file (and which language are they in). temp_dir = tempfile.mkdtemp(dir=config.temp_dir) for file_ in files: temp_filename = os.path.join( temp_dir, file_["filename"].replace("%l", language)) shutil.copy( os.path.join(self.import_source, "files", files[0]["digest"]), temp_filename) file_["filename"] = temp_filename filename = os.path.join(files[0]["filename"]) browser = Browser() browser.set_handle_robots(False) step( LoginRequest(browser, username, password, base_url=self.cws_address)) step( SubmitRequest(browser, (int(t_id), t_short), filename=filename, base_url=self.cws_address)) shutil.rmtree(temp_dir) def token(self, timestamp, username, password, t_id, t_short, submission_num): """Execute the request for releasing test a submission. timestamp (int): seconds from the start. username (string): username issuing the submission. password (string): password of username. t_id (string): id of the task. t_short (string): short name of the task. submission_num (string): id of the submission to release test. """ logger.info("%s - Playing token for %s on task %s" % (to_time(timestamp), username, t_short)) browser = Browser() browser.set_handle_robots(False) step( LoginRequest(browser, username, password, base_url=self.cws_address)) step( TokenRequest(browser, (int(t_id), t_short), submission_num=submission_num, base_url=self.cws_address)) def replay(self): """Start replaying the events in source on the CWS at the specified address. """ with self.speed_lock: index = 0 if self.start_from is not None: while index < len(self.events) \ and float(self.events[index][0]) < self.start_from: index += 1 self.start = time.time() - self.start_from else: self.start = time.time() while index < len(self.events): timestamp, username, password, task_id, task_name, type_, data \ = self.events[index] to_wait = (timestamp / self.speed - (time.time() - self.start)) while to_wait > .5: if 0 < to_wait % 10 <= .5: logger.info("Next event in %d seconds." % int(to_wait)) time.sleep(.5) to_wait = (timestamp / self.speed - (time.time() - self.start)) if to_wait > 0: time.sleep(to_wait) if type_ == "s": # Submit. files, language = data self.submit(timestamp=timestamp, username=username, password=password, t_id=task_id, t_short=task_name, files=files, language=language) elif type_ == "t": # Token. self.token(timestamp=timestamp, username=username, password=password, t_id=task_id, t_short=task_name, submission_num=data) else: logger.warning("Unexpected type `%s', ignoring." % type_) index += 1