Example #1
0
    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)
Example #2
0
    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()
Example #3
0
    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)
Example #4
0
    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()
Example #5
0
    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)
Example #6
0
    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)
Example #7
0
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
Example #8
0
 def call(self):
     return task_score(self.participation, self.task)
Example #9
0
    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)
Example #10
0
    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)