def get(self, task_name): task = self.get_task(task_name) if task is None: raise tornado.web.HTTPError(404) participation_joined = self.sql_session.query(Participation)\ .filter(Participation.id == self.current_user.id)\ .options(joinedload("submissions"))\ .options(joinedload("submissions.token"))\ .options(joinedload("submissions.results"))\ .first() score, partial = task_score(participation_joined, task) score = round(score, task.score_precision) score_string = format(score, ".%df" % task.score_precision) self.write(score_string)
def export_ranking(self): """Exports the ranking in csv and txt (human-readable) form. """ logger.info("Exporting ranking.") # Create the structure to store the scores. scores = dict((user.username, 0.0) for user in self.contest.users if not user.hidden) task_scores = dict((task.id, dict((user.username, 0.0) for user in self.contest.users if not user.hidden)) for task in self.contest.tasks) is_partial = False for task in self.contest.tasks: for user in self.contest.users: if user.hidden: continue score, partial = task_score(user, task) is_partial = is_partial or partial task_scores[task.id][user.username] = score scores[user.username] += score if is_partial: logger.warning("Some of the scores are not definitive.") sorted_usernames = sorted(scores.keys(), key=lambda username: (scores[username], username), reverse=True) sorted_tasks = sorted(self.contest.tasks, key=lambda task: task.num) ranking_file = io.open( os.path.join(self.spool_dir, "ranking.txt"), "w", encoding="utf-8") ranking_csv = io.open( os.path.join(self.spool_dir, "ranking.csv"), "w", encoding="utf-8") # Write rankings' header. n_tasks = len(sorted_tasks) print("Final Ranking of Contest `%s'" % self.contest.description, file=ranking_file) points_line = " %10s" * n_tasks csv_points_line = ",%s" * n_tasks print(("%20s %10s" % ("User", "Total")) + (points_line % tuple([t.name for t in sorted_tasks])), file=ranking_file) print(("%s,%s" % ("user", "total")) + (csv_points_line % tuple([t.name for t in sorted_tasks])), file=ranking_csv) # Write rankings' content. points_line = " %10.3f" * n_tasks csv_points_line = ",%.6f" * n_tasks for username in sorted_usernames: user_scores = [task_scores[task.id][username] for task in sorted_tasks] print(("%20s %10.3f" % (username, scores[username])) + (points_line % tuple(user_scores)), file=ranking_file) print(("%s,%.6f" % (username, scores[username])) + (csv_points_line % tuple(user_scores)), file=ranking_csv) ranking_file.close() ranking_csv.close()
def get(self, contest_id, format="online"): # This validates the contest id. self.safe_get_item(Contest, contest_id) logger.debug("ranking computation: start") # This massive joined load gets all the information which we will need # to generating the rankings. self.contest = self.sql_session.query(Contest)\ .filter(Contest.id == contest_id)\ .options(subqueryload('participations'))\ .options(subqueryload('participations.submissions'))\ .options(subqueryload('participations.submissions.token'))\ .options(subqueryload('participations.submissions.results'))\ .options(defer('participations.submissions.results.score_details'))\ .options(defer('participations.submissions.results.public_score_details'))\ .first() logger.debug("ranking computation: data load completed") # Preprocess participations: get data about teams, scores show_teams = False for p in self.contest.participations: show_teams = show_teams or p.team_id p.scores = [] total_score = 0.0 partial = False for task in self.contest.tasks: t_score, t_partial = task_score(p, task) t_score = round(t_score, task.score_precision) p.scores.append((t_score, t_partial)) total_score += t_score partial = partial or t_partial total_score = round(total_score, self.contest.score_precision) p.total_score = (total_score, partial) logger.debug("ranking computation: completed") self.r_params = self.render_params() self.r_params["show_teams"] = show_teams if format == "txt": self.set_header("Content-Type", "text/plain") self.set_header("Content-Disposition", "attachment; filename=\"ranking.txt\"") self.render("ranking.txt", **self.r_params) elif format == "csv": self.set_header("Content-Type", "text/csv") self.set_header("Content-Disposition", "attachment; filename=\"ranking.csv\"") if six.PY3: output = io.StringIO() # untested else: # In python2 we must use this because its csv module does not # support unicode input output = io.BytesIO() writer = csv.writer(output) include_partial = True contest = self.r_params["contest"] row = ["Username", "User"] if show_teams: row.append("Team") for task in contest.tasks: row.append(task.name) if include_partial: row.append("P") row.append("Global") if include_partial: row.append("P") writer.writerow(row) for p in sorted(contest.participations, key=lambda p: p.total_score, reverse=True): if p.hidden: continue row = [p.user.username, "%s %s" % (p.user.first_name, p.user.last_name)] if show_teams: row.append(p.team.name if p.team else "") assert len(contest.tasks) == len(p.scores) for t_score, t_partial in p.scores: # Custom field, see above row.append(t_score) if include_partial: row.append("*" if t_partial else "") total_score, partial = p.total_score # Custom field, see above row.append(total_score) if include_partial: row.append("*" if partial else "") if six.PY3: writer.writerow(row) # untested else: writer.writerow([s.encode("utf-8") for s in row]) self.finish(output.getvalue()) else: self.render("ranking.html", **self.r_params)
def export_ranking(self): """Exports the ranking in csv and txt (human-readable) form. """ logger.info("Exporting ranking.") # Create the structure to store the scores. scores = dict((participation.user.username, 0.0) for participation in self.contest.participations if not participation.hidden) task_scores = dict( (task.id, dict((participation.user.username, 0.0) for participation in self.contest.participations if not participation.hidden)) for task in self.contest.tasks) is_partial = False for task in self.contest.tasks: for participation in self.contest.participations: if participation.hidden: continue score, partial = task_score(participation, task) is_partial = is_partial or partial task_scores[task.id][participation.user.username] = score scores[participation.user.username] += score if is_partial: logger.warning("Some of the scores are not definitive.") sorted_usernames = sorted(iterkeys(scores), key=lambda username: (scores[username], username), reverse=True) sorted_tasks = sorted(self.contest.tasks, key=lambda task: task.num) ranking_file = io.open(os.path.join(self.spool_dir, "ranking.txt"), "w", encoding="utf-8") ranking_csv = io.open(os.path.join(self.spool_dir, "ranking.csv"), "w", encoding="utf-8") # Write rankings' header. n_tasks = len(sorted_tasks) print("Final Ranking of Contest `%s'" % self.contest.description, file=ranking_file) points_line = " %10s" * n_tasks csv_points_line = ",%s" * n_tasks print(("%20s %10s" % ("User", "Total")) + (points_line % tuple([t.name for t in sorted_tasks])), file=ranking_file) print(("%s,%s" % ("user", "total")) + (csv_points_line % tuple([t.name for t in sorted_tasks])), file=ranking_csv) # Write rankings' content. points_line = " %10.3f" * n_tasks csv_points_line = ",%.6f" * n_tasks for username in sorted_usernames: user_scores = [ task_scores[task.id][username] for task in sorted_tasks ] print(("%20s %10.3f" % (username, scores[username])) + (points_line % tuple(user_scores)), file=ranking_file) print(("%s,%.6f" % (username, scores[username])) + (csv_points_line % tuple(user_scores)), file=ranking_csv) ranking_file.close() ranking_csv.close()
def get(self, contest_id, format="online"): # This validates the contest id. self.safe_get_item(Contest, contest_id) # This massive joined load gets all the information which we will need # to generating the rankings. self.contest = self.sql_session.query(Contest)\ .filter(Contest.id == contest_id)\ .options(joinedload('participations'))\ .options(joinedload('participations.submissions'))\ .options(joinedload('participations.submissions.token'))\ .options(joinedload('participations.submissions.results'))\ .first() self.r_params = self.render_params() if format == "txt": self.set_header("Content-Type", "text/plain") self.set_header("Content-Disposition", "attachment; filename=\"ranking.txt\"") self.render("ranking.txt", **self.r_params) elif format == "csv": self.set_header("Content-Type", "text/csv") self.set_header("Content-Disposition", "attachment; filename=\"ranking.csv\"") if sys.version_info >= (3, 0): output = io.StringIO() # untested else: # In python2 we must use this because its csv module does not # support unicode input output = io.BytesIO() writer = csv.writer(output) include_partial = True contest = self.r_params["contest"] row = ["Username", "User"] for task in contest.tasks: row.append(task.name) if include_partial: row.append("P") row.append("Global") if include_partial: row.append("P") writer.writerow(row) for p in sorted(contest.participations, key=lambda p: p.user.username): if p.hidden: continue score = 0.0 partial = False row = [ p.user.username, "%s %s" % (p.user.first_name, p.user.last_name) ] for task in contest.tasks: t_score, t_partial = task_score(p, task) t_score = round(t_score, task.score_precision) score += t_score partial = partial or t_partial row.append(t_score) if include_partial: row.append("*" if t_partial else "") row.append(round(score, contest.score_precision)) if include_partial: row.append("*" if partial else "") if sys.version_info >= (3, 0): writer.writerow(row) # untested else: writer.writerow([unicode(s).encode("utf-8") for s in row]) self.finish(output.getvalue()) else: self.render("ranking.html", **self.r_params)
def get(self, contest_id, format="online"): # This validates the contest id. self.safe_get_item(Contest, contest_id) # This massive joined load gets all the information which we will need # to generating the rankings. self.contest = self.sql_session.query(Contest)\ .filter(Contest.id == contest_id)\ .options(joinedload('participations'))\ .options(joinedload('participations.submissions'))\ .options(joinedload('participations.submissions.token'))\ .options(joinedload('participations.submissions.results'))\ .first() self.r_params = self.render_params() if format == "txt": self.set_header("Content-Type", "text/plain") self.set_header("Content-Disposition", "attachment; filename=\"ranking.txt\"") self.render("ranking.txt", **self.r_params) elif format == "csv": self.set_header("Content-Type", "text/csv") self.set_header("Content-Disposition", "attachment; filename=\"ranking.csv\"") if sys.version_info >= (3, 0): output = io.StringIO() # untested else: # In python2 we must use this because its csv module does not # support unicode input output = io.BytesIO() writer = csv.writer(output) include_partial = True contest = self.r_params["contest"] row = ["Username", "User"] for task in contest.tasks: row.append(task.name) if include_partial: row.append("P") row.append("Global") if include_partial: row.append("P") writer.writerow(row) for p in sorted(contest.participations, key=lambda p: p.user.username): if p.hidden: continue score = 0.0 partial = False row = [p.user.username, "%s %s" % (p.user.first_name, p.user.last_name)] for task in contest.tasks: t_score, t_partial = task_score(p, task) t_score = round(t_score, task.score_precision) score += t_score partial = partial or t_partial row.append(t_score) if include_partial: row.append("*" if t_partial else "") row.append(round(score, contest.score_precision)) if include_partial: row.append("*" if partial else "") if sys.version_info >= (3, 0): writer.writerow(row) # untested else: writer.writerow([unicode(s).encode("utf-8") for s in row]) self.finish(output.getvalue()) else: self.render("ranking.html", **self.r_params)
def create_ranks_object(included_contests=None, excluded_contests=None, included_tasks=None, excluded_tasks=None, included_users=None, excluded_users=None): """ Create a ranks object with the given parameters. """ with SessionGen() as session: # Fetch all relevant data. contests = get_contests(session, included_contests, excluded_contests) tasks = get_tasks(session, included_tasks, excluded_tasks) name_to_task = {task.name: task for task in tasks} users = get_users(session, included_users, excluded_users) usernames_set = set(user.username for user in users) result = {"contests": [], "scores": {}} # Initialize users info. for username in usernames_set: result["scores"][username] = {} # Fill the result according to each contest. for contest in contests: # Relevant tasks only. contest_tasks = [task.name for task in contest.tasks if task.name in name_to_task] # Don't include empty contests (where all tasks were excluded). if not contest_tasks: continue # Contest information. result["contests"] += [{ "name": contest.name, "tasks": contest_tasks }] # Submission information for each user and each task. for participation in contest.participations: username = participation.user.username # Skip irrelevant users. if username not in usernames_set: continue # Get the tasks this user submitted to. submitted_task_names = set(submission.task.name for submission in participation.submissions) for task_name in contest_tasks: # If the user did not submit to this task, we don't write # anything (this is distinct from getting 0). if task_name not in submitted_task_names: continue task = name_to_task[task_name] score, partial = task_score(participation, task) score = round(score, task.score_precision) score_string = str(score) if partial: score_string += "*" result["scores"][username][task_name] = score_string return result
def call(self): return task_score(self.participation, self.task)
def get(self, contest_id, format="online"): # This validates the contest id. self.safe_get_item(Contest, contest_id) # This massive joined load gets all the information which we will need # to generating the rankings. self.contest = self.sql_session.query(Contest)\ .filter(Contest.id == contest_id)\ .options(joinedload('participations'))\ .options(joinedload('participations.submissions'))\ .options(joinedload('participations.submissions.token'))\ .options(joinedload('participations.submissions.results'))\ .first() # Preprocess participations: get data about teams, scores show_teams = False for p in self.contest.participations: show_teams = show_teams or p.team_id p.scores = [] total_score = 0.0 partial = False for task in self.contest.tasks: t_score, t_partial = task_score(p, task) t_score = round(t_score, task.score_precision) p.scores.append((t_score, t_partial)) total_score += t_score partial = partial or t_partial total_score = round(total_score, self.contest.score_precision) p.total_score = (total_score, partial) self.r_params = self.render_params() self.r_params["show_teams"] = show_teams if format == "txt": self.set_header("Content-Type", "text/plain") self.set_header("Content-Disposition", "attachment; filename=\"ranking.txt\"") self.render("ranking.txt", **self.r_params) elif format == "csv": self.set_header("Content-Type", "text/csv") self.set_header("Content-Disposition", "attachment; filename=\"ranking.csv\"") if six.PY3: output = io.StringIO() # untested else: # In python2 we must use this because its csv module does not # support unicode input output = io.BytesIO() writer = csv.writer(output) include_partial = True contest = self.r_params["contest"] row = ["Username", "User"] if show_teams: row.append("Team") for task in contest.tasks: row.append(task.name) if include_partial: row.append("P") row.append("Global") if include_partial: row.append("P") writer.writerow(row) for p in sorted(contest.participations, key=lambda p: p.total_score, reverse=True): if p.hidden: continue row = [p.user.username, "%s %s" % (p.user.first_name, p.user.last_name)] if show_teams: row.append(p.team.name if p.team else "") assert len(contest.tasks) == len(p.scores) for t_score, t_partial in p.scores: # Custom field, see above row.append(t_score) if include_partial: row.append("*" if t_partial else "") total_score, partial = p.total_score # Custom field, see above row.append(total_score) if include_partial: row.append("*" if partial else "") if six.PY3: writer.writerow(row) # untested else: writer.writerow([s.encode("utf-8") for s in row]) self.finish(output.getvalue()) else: self.render("ranking.html", **self.r_params)
def get(self): redis_stats_key = '{}contest:{}:stats'.format(config.redis_prefix, self.contest.id) redis_lock_key = '{}contest:{}:stats_lock'.format( config.redis_prefix, self.contest.id) redis_update_key = '{}contest:{}:stats_update'.format( config.redis_prefix, self.contest.id) redis_lock = None if self.redis_conn: redis_lock = self.redis_conn.lock(redis_lock_key, timeout=30) while True: stats_cache = self.redis_conn.get(redis_stats_key) if stats_cache is not None: self.set_header( 'Cache-Control', 'max-age={}'.format(CLIENT_STATS_CACHE_TTL)) self.write(stats_cache) return if redis_lock.acquire(blocking=False): break with closing( self.redis_conn.pubsub( ignore_subscribe_messages=True)) as pubsub: pubsub.subscribe(redis_update_key) pubsub.get_message(timeout=30) pubsub.unsubscribe() contest = self.sql_session.query(Contest)\ .filter(Contest.id == self.contest.id)\ .options(subqueryload('participations'))\ .options(subqueryload('participations.submissions'))\ .options(subqueryload('participations.submissions.token'))\ .options(subqueryload('participations.submissions.results'))\ .options(defer('participations.submissions.results.score_details'))\ .options(defer('participations.submissions.results.public_score_details'))\ .first() score_list = [] for task in contest.tasks: name = task.name task_total = 0.0 for p in contest.participations: if p.hidden: continue t_score, task_partial = task_score(p, task) t_score = round(t_score, task.score_precision) task_total += t_score score_list.append({'name': name, 'total': task_total}) contest_total = sum(t['total'] for t in score_list) def compute_ratio(score_sum): if contest_total == 0: return 1.0 / len(contest.tasks) return score_sum / contest_total stats = [{ 'name': t['name'], 'ratio': round(compute_ratio(t['total']), 2) } for t in score_list] stats_text = json.dumps({'tasks_by_score_rel': stats}) if self.redis_conn: self.redis_conn.set(redis_stats_key, stats_text, ex=REDIS_STATS_CACHE_TTL) self.redis_conn.publish(redis_update_key, 'updated') redis_lock.release() self.set_header('Cache-Control', 'max-age={}'.format(CLIENT_STATS_CACHE_TTL)) self.write(stats_text)