Exemple #1
0
    def add_task_score(self, participation, task, data):
        """Add the task score information to the dict to be returned.

        participation (Participation): user for which we want the score.
        task (Task): task for which we want the score.
        data (dict): where to put the data; all fields will start with "task",
            followed by "public" if referring to the public scores, or
            "tokened" if referring to the total score (always limited to
            tokened submissions); for both public and tokened, the fields are:
            "score" and "score_message"; in addition we have
            "task_is_score_partial" as partial info is the same for both.

        """
        # Just to preload all information required to compute the task score.
        self.sql_session.query(Submission)\
            .filter(Submission.participation == participation)\
            .filter(Submission.task == task)\
            .options(joinedload(Submission.token))\
            .options(joinedload(Submission.results))\
            .all()
        data["task_public_score"], public_score_is_partial = \
            task_score(participation, task, public=True, rounded=True)
        data["task_tokened_score"], tokened_score_is_partial = \
            task_score(participation, task, only_tokened=True, rounded=True)
        # These two should be the same, anyway.
        data["task_score_is_partial"] = \
            public_score_is_partial or tokened_score_is_partial

        score_type = task.active_dataset.score_type_object
        data["task_public_score_message"] = score_type.format_score(
            data["task_public_score"], score_type.max_public_score, None,
            task.score_precision, translation=self.translation)
        data["task_tokened_score_message"] = score_type.format_score(
            data["task_tokened_score"], score_type.max_score, None,
            task.score_precision, translation=self.translation)
Exemple #2
0
    def add_task_score(self, participation, task, data):
        """Add the task score information to the dict to be returned.

        participation (Participation): user for which we want the score.
        task (Task): task for which we want the score.
        data (dict): where to put the data; all fields will start with "task",
            followed by "public" if referring to the public scores, or
            "tokened" if referring to the total score (always limited to
            tokened submissions); for both public and tokened, the fields are:
            "score" and "score_message"; in addition we have
            "task_is_score_partial" as partial info is the same for both.

        """
        # Just to preload all information required to compute the task score.
        self.sql_session.query(Submission)\
            .filter(Submission.participation == participation)\
            .filter(Submission.task == task)\
            .options(joinedload(Submission.token))\
            .options(joinedload(Submission.results))\
            .all()
        data["task_public_score"], public_score_is_partial = \
            task_score(participation, task, public=True)
        data["task_tokened_score"], tokened_score_is_partial = \
            task_score(participation, task, only_tokened=True)
        # These two should be the same, anyway.
        data["task_score_is_partial"] = \
            public_score_is_partial or tokened_score_is_partial

        score_type = task.active_dataset.score_type_object
        data["task_public_score_message"] = score_type.format_score(
            data["task_public_score"], score_type.max_public_score, None,
            task.score_precision, translation=self.translation)
        data["task_tokened_score_message"] = score_type.format_score(
            data["task_tokened_score"], score_type.max_score, None,
            task.score_precision, translation=self.translation)
Exemple #3
0
    def get(self, task_name):
        participation = self.current_user

        task = self.get_task(task_name)
        if task is None:
            raise tornado.web.HTTPError(404)

        submissions = self.sql_session.query(Submission)\
            .filter(Submission.participation == participation)\
            .filter(Submission.task == task)\
            .options(joinedload(Submission.token))\
            .options(joinedload(Submission.results))\
            .all()

        public_score, is_public_score_partial = task_score(
            participation, task, public=True)
        tokened_score, is_tokened_score_partial = task_score(
            participation, task, only_tokened=True)
        # These two should be the same, anyway.
        is_score_partial = is_public_score_partial or is_tokened_score_partial

        submissions_left_contest = None
        if self.contest.max_submission_number is not None:
            submissions_c = \
                get_submission_count(self.sql_session, participation,
                                     contest=self.contest)
            submissions_left_contest = \
                self.contest.max_submission_number - submissions_c

        submissions_left_task = None
        if task.max_submission_number is not None:
            submissions_left_task = \
                task.max_submission_number - len(submissions)

        submissions_left = submissions_left_contest
        if submissions_left_task is not None and \
            (submissions_left_contest is None or
             submissions_left_contest > submissions_left_task):
            submissions_left = submissions_left_task

        # Make sure we do not show negative value if admins changed
        # the maximum
        if submissions_left is not None:
            submissions_left = max(0, submissions_left)

        tokens_info = tokens_available(participation, task, self.timestamp)

        download_allowed = self.contest.submissions_download_allowed
        self.render("task_submissions.html",
                    task=task, submissions=submissions,
                    public_score=public_score,
                    tokened_score=tokened_score,
                    is_score_partial=is_score_partial,
                    tokens_task=task.token_mode,
                    tokens_info=tokens_info,
                    submissions_left=submissions_left,
                    submissions_download_allowed=download_allowed,
                    **self.r_params)
Exemple #4
0
    def get(self, task_name):
        participation = self.current_user

        task = self.get_task(task_name)
        if task is None:
            raise tornado.web.HTTPError(404)

        submissions = self.sql_session.query(Submission)\
            .filter(Submission.participation == participation)\
            .filter(Submission.task == task)\
            .options(joinedload(Submission.token))\
            .options(joinedload(Submission.results))\
            .all()

        public_score, is_public_score_partial = task_score(
            participation, task, public=True, rounded=True)
        tokened_score, is_tokened_score_partial = task_score(
            participation, task, only_tokened=True, rounded=True)
        # These two should be the same, anyway.
        is_score_partial = is_public_score_partial or is_tokened_score_partial

        submissions_left_contest = None
        if self.contest.max_submission_number is not None:
            submissions_c = \
                get_submission_count(self.sql_session, participation,
                                     contest=self.contest)
            submissions_left_contest = \
                self.contest.max_submission_number - submissions_c

        submissions_left_task = None
        if task.max_submission_number is not None:
            submissions_left_task = \
                task.max_submission_number - len(submissions)

        submissions_left = submissions_left_contest
        if submissions_left_task is not None and \
            (submissions_left_contest is None or
             submissions_left_contest > submissions_left_task):
            submissions_left = submissions_left_task

        # Make sure we do not show negative value if admins changed
        # the maximum
        if submissions_left is not None:
            submissions_left = max(0, submissions_left)

        tokens_info = tokens_available(participation, task, self.timestamp)

        download_allowed = self.contest.submissions_download_allowed
        self.render("task_submissions.html",
                    task=task, submissions=submissions,
                    public_score=public_score,
                    tokened_score=tokened_score,
                    is_score_partial=is_score_partial,
                    tokens_task=task.token_mode,
                    tokens_info=tokens_info,
                    submissions_left=submissions_left,
                    submissions_download_allowed=download_allowed,
                    **self.r_params)
Exemple #5
0
    def get(self):

        # contest scoring
        participation = self.current_user
        has_public_scores = False
        has_tokened_scores = False
        participation_scores = dict()
        for task in self.contest.tasks:
            score_type = task.active_dataset.score_type_object
            if score_type:
                if score_type.max_public_score > 0:
                    has_public_scores = True
                if score_type.max_public_score < score_type.max_score:
                    has_tokened_scores = True

                # current user's scores
                if participation:
                    # check for submission
                    submissions = self.sql_session.query(Submission)\
                        .filter(Submission.participation == participation)\
                        .filter(Submission.task == task)\
                        .options(joinedload(Submission.token))\
                        .options(joinedload(Submission.results))\
                        .all()
                    num_submissions = len(submissions)

                    public_score, is_public_score_partial = task_score(
                        participation, task, public=True, rounded=True)
                    tokened_score, is_tokened_score_partial = task_score(
                        participation, task, only_tokened=True, rounded=True)
                    is_score_partial = is_public_score_partial or is_tokened_score_partial

                    participation_scores[task.name] = {
                        'public_score': public_score,
                        'tokened_score': tokened_score,
                        'is_score_partial': is_score_partial,
                        'num_submissions': num_submissions
                    }

        self.r_params['has_public_scores'] = has_public_scores
        self.r_params['has_tokened_scores'] = has_tokened_scores
        self.r_params['participation_scores'] = participation_scores
        self.render("overview.html", **self.r_params)
Exemple #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()

        # 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)
Exemple #7
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, rounded=True)
                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\"")

            output = io.StringIO()  # untested
            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 "")

                writer.writerow(row)  # untested

            self.finish(output.getvalue())
        else:
            self.render("ranking.html", **self.r_params)
Exemple #8
0
    def get(self, contest_id):
        # 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.
        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()

        r_params = {
            'contest': contest,
            'format_score': DetailedResultsHandler.__format_score
        }
        partial_results = False
        results = []

        max_score = 0
        for task in contest.tasks:
            dataset = task.active_dataset
            scoretype = dataset.score_type_object
            max_score += scoretype.max_score

        for p in sorted(contest.participations,
                        key=lambda p: (locale.strxfrm(p.user.last_name),
                                       locale.strxfrm(p.user.first_name))):
            if p.hidden:
                continue

            result = {'participation': p, 'max_score': max_score}
            total_score = 0
            task_results = []
            for task in contest.tasks:
                submission = ScoredSubmission()
                score, partial = task_score(p,
                                            task,
                                            rounded=True,
                                            submission=submission)
                if partial:
                    partial_results = True
                st = task.active_dataset.score_type_object
                if not isinstance(st, ScoreTypeGroup):
                    raise Exception(
                        "Unsupported score type for task {}".format(task.name))

                task_max_score = st.max_score
                total_score += score

                test_results = []
                if submission.s:
                    sr = submission.s.get_result(task.active_dataset)
                    if sr:
                        status = sr.get_status()
                    else:
                        status = SubmissionResult.COMPILING

                    if status == SubmissionResult.SCORED:
                        test_results = sr.score_details
                        for group in test_results:
                            for testcase in group['testcases']:
                                testcase['text'] = DetailedResultsHandler\
                                        .__get_result(testcase['text'])
                else:  # skip tasks with no submissions
                    assert score == 0.0
                    continue

                task_result = {
                    'task': task,
                    'score': score,
                    'max_score': task_max_score,
                    'status': status,
                    'test_results': test_results
                }

                task_results.append(task_result)
            result['total_score'] = total_score
            result['tasks'] = task_results

            results.append(result)

        r_params['results'] = results
        r_params['partial_results'] = partial_results

        self.set_header('Content-Type', 'text/html')
        self.set_header('Content-Disposition',
                        'attachment; filename="detailed_results.html"')
        self.render('detailed_results.html', **r_params)
Exemple #9
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(scores.keys(),
                                  key=lambda username: (scores[username],
                                                        username),
                                  reverse=True)
        sorted_tasks = sorted(self.contest.tasks,
                              key=lambda task: task.num)

        with open(os.path.join(self.spool_dir, "ranking.txt"),
                  "wt", encoding="utf-8") as ranking_file, \
                open(os.path.join(self.spool_dir, "ranking.csv"),
                     "wt", encoding="utf-8") as ranking_csv:

            # 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)
Exemple #10
0
 def call(self):
     return task_score(self.participation, self.task)
Exemple #11
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 generate 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)

            # Calculate the time when the score of the participation last
            # increased
            p.last_progress = participation_last_progress(p)

        self.r_params = self.render_params()
        contest = self.r_params["contest"]

        def participation_key(p):
            try:
                return (-p.total_score[0], p.last_progress)
            except Exception:
                logger.warning("Cannot rank user %s" % p.user.username)
                return (0, p.last_progress)

        sorted_participations = sorted(contest.participations,
                                       key=participation_key)
        self.r_params["sorted_participations"] = sorted_participations

        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 = False

            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")

            row.append("Last progress")

            writer.writerow(row)

            for p in sorted_participations:
                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(str(t_score))
                    if include_partial:
                        row.append("*" if t_partial else "")

                total_score, partial = p.total_score  # Custom field, see above
                row.append(str(total_score))
                if include_partial:
                    row.append("*" if partial else "")

                if total_score > 0:
                    timestamp = int(
                        time.mktime(p.last_progress.timetuple()) +
                        p.last_progress.microsecond / 1000000.0)
                    row.append(str(timestamp))
                else:
                    row.append("")

                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)
Exemple #12
0
    def get(self, contest_id, format="online"):
        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.set_header("Content-Type", "application/zip")
        self.set_header("Content-Disposition",
                        "attachment; filename=\"users_data.zip\"")

        shutil.rmtree(BASE_PATH, ignore_errors=True)

        fc = FileCacher()

        for p in self.contest.participations:
            path = "%s/%s/" % (BASE_PATH, p.user.username)
            os.makedirs(path)

            # Identify all the files submitted by the user for each task
            task_sr = defaultdict(list)
            for sub in p.submissions:
                sub_sr = sub.get_result(sub.task.active_dataset)

                file = sub.files.items()[0][1]
                filename = file.filename
                if sub.language is not None:
                    filename = filename.replace(
                        ".%l",
                        get_language(sub.language).source_extension)
                if sub_sr.score:
                    task_sr[sub.task_id].append(
                        (sub_sr.score, sub.timestamp, (filename, file.digest)))

            # Select the last file submitted with maximum score for each task
            task_last_best = [
                sorted(task_sr[tid], key=lambda x: (x[0], x[1]),
                       reverse=True)[0][2] for tid in task_sr
            ]

            # Write the selected file for each task
            for filename, digest in task_last_best:
                file_content = fc.get_file(digest).read()
                with open("%s%s" % (path, filename), "w") as f:
                    f.write(file_content.decode("utf8"))

            # Find the users' scores for each task
            scores = []
            for task in self.contest.tasks:
                t_score, _ = task_score(p, task)
                t_score = round(t_score, task.score_precision)
                scores.append((task.id, t_score))

            # Find the users' last progress for each task
            task_last_progress = {
                tid: sorted(task_sr[tid], key=lambda x: (-x[0], x[1]))[0][1]
                for tid in task_sr
            }

            # Write a csv with some information on the participation
            info_csv = [["Username", "User"]]
            for task in self.contest.tasks:
                info_csv[0].append("%s (score)" % task.name)
                info_csv[0].append("%s (last progress)" % task.name)
            info_csv[0].append("Last progress")
            full_name = "%s %s" % (p.user.first_name, p.user.last_name)
            info_csv.append(
                [p.user.username.encode('utf-8'),
                 full_name.encode('utf-8')])
            for tid, t_score in scores:
                info_csv[1].append(str(t_score))
                if tid in task_last_progress:
                    last_progress = \
                        task_last_progress[tid].strftime('%H:%M:%S')
                else:
                    last_progress = ""
                info_csv[1].append(last_progress)
            if task_last_progress:
                last_progress_overall_ts = max(task_last_progress.values())
                last_progress_overall = \
                    last_progress_overall_ts.strftime('%H:%M:%S')
            else:
                last_progress_overall = ""
            info_csv[1].append(last_progress_overall)
            with open("%sinfo.csv" % path, "wb") as f_out:
                csv_writer = csv.writer(f_out)
                for row in info_csv:
                    csv_writer.writerow(row)

        # Create a downloadable archive will all this data
        shutil.make_archive("users_data", "zip", ".", "users_data")

        output = open("users_data.zip", "rb", buffering=0)

        self.finish(output.read())
Exemple #13
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()
Exemple #14
0
    def get(self, contest_id, format="online"):
        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.set_header("Content-Type", "application/zip")
        self.set_header("Content-Disposition",
                        "attachment; filename=\"users_data.zip\"")

        shutil.rmtree(BASE_PATH, ignore_errors=True)

        fc = FileCacher()

        for p in self.contest.participations:
            path = "%s/%s/" % (BASE_PATH, p.user.username)
            os.makedirs(path)

            # Identify all the files submitted by the user for each task
            task_sr = defaultdict(list)
            for sub in p.submissions:
                sub_sr = sub.get_result(sub.task.active_dataset)

                file = sub.files.items()[0][1]
                filename = file.filename
                if sub.language is not None:
                    filename = filename.replace(
                        ".%l", get_language(sub.language).source_extension)
                if sub_sr.score:
                    task_sr[sub.task_id].append(
                        (sub_sr.score, sub.timestamp, (filename, file.digest)))

            # Select the last file submitted with maximum score for each task
            task_last_best = [
                sorted(task_sr[tid], key=lambda x: (x[0], x[1]),
                       reverse=True)[0][2]
                for tid in task_sr
            ]

            # Write the selected file for each task
            for filename, digest in task_last_best:
                file_content = fc.get_file(digest).read()
                with open("%s%s" % (path, filename), "w") as f:
                    f.write(file_content.decode("utf8"))

            # Find the users' scores for each task
            scores = []
            for task in self.contest.tasks:
                t_score, _ = task_score(p, task)
                t_score = round(t_score, task.score_precision)
                scores.append((task.id, t_score))

            # Find the users' last progress for each task
            task_last_progress = {
                tid:
                sorted(task_sr[tid], key=lambda x: (-x[0], x[1]))[0][1]
                for tid in task_sr
            }

            # Write a csv with some information on the participation
            info_csv = [["Username", "User"]]
            for task in self.contest.tasks:
                info_csv[0].append("%s (score)" % task.name)
                info_csv[0].append("%s (last progress)" % task.name)
            info_csv[0].append("Last progress")
            full_name = "%s %s" % (p.user.first_name, p.user.last_name)
            info_csv.append([p.user.username.encode('utf-8'),
                             full_name.encode('utf-8')])
            for tid, t_score in scores:
                info_csv[1].append(str(t_score))
                if tid in task_last_progress:
                    last_progress = \
                        task_last_progress[tid].strftime('%H:%M:%S')
                else:
                    last_progress = ""
                info_csv[1].append(last_progress)
            if task_last_progress:
                last_progress_overall_ts = max(task_last_progress.values())
                last_progress_overall = \
                    last_progress_overall_ts.strftime('%H:%M:%S')
            else:
                last_progress_overall = ""
            info_csv[1].append(last_progress_overall)
            with open("%sinfo.csv" % path, "wb") as f_out:
                csv_writer = csv.writer(f_out)
                for row in info_csv:
                    csv_writer.writerow(row)

        # Create a downloadable archive will all this data
        shutil.make_archive("users_data", "zip", ".", "users_data")

        output = open("users_data.zip", "rb", buffering=0)

        self.finish(output.read())
Exemple #15
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 generate 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)

            # Calculate the time when the score of the participation last
            # increased
            p.last_progress = participation_last_progress(p)

        self.r_params = self.render_params()
        contest = self.r_params["contest"]

        def participation_key(p):
            try:
                return (-p.total_score[0], p.last_progress)
            except Exception:
                logger.warning("Cannot rank user %s" % p.user.username)
                return (0, p.last_progress)

        sorted_participations = sorted(
            contest.participations, key=participation_key)
        self.r_params["sorted_participations"] = sorted_participations

        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 = False

            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")

            row.append("Last progress")

            writer.writerow(row)

            for p in sorted_participations:
                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(str(t_score))
                    if include_partial:
                        row.append("*" if t_partial else "")

                total_score, partial = p.total_score  # Custom field, see above
                row.append(str(total_score))
                if include_partial:
                    row.append("*" if partial else "")

                if total_score > 0:
                    timestamp = int(
                        time.mktime(p.last_progress.timetuple())
                        + p.last_progress.microsecond / 1000000.0)
                    row.append(str(timestamp))
                else:
                    row.append("")

                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)
Exemple #16
0
    def append_task_render_params(self, task_name, rparams):
        """Append task-specific render parameters
        """
        participation = self.current_user

        task = self.get_task(task_name)
        if task is None:
            raise tornado.web.HTTPError(404)

        submissions = self.sql_session.query(Submission)\
            .filter(Submission.participation == participation)\
            .filter(Submission.task == task)\
            .options(joinedload(Submission.token))\
            .options(joinedload(Submission.results))\
            .all()

        public_score, is_public_score_partial = task_score(participation,
                                                           task,
                                                           public=True,
                                                           rounded=True)
        tokened_score, is_tokened_score_partial = task_score(participation,
                                                             task,
                                                             only_tokened=True,
                                                             rounded=True)
        # These two should be the same, anyway.
        is_score_partial = is_public_score_partial or is_tokened_score_partial

        submissions_left_contest = None
        if self.contest.max_submission_number is not None:
            submissions_c = \
                get_submission_count(self.sql_session, participation,
                                     contest=self.contest)
            submissions_left_contest = \
                self.contest.max_submission_number - submissions_c

        submissions_left_task = None
        if task.max_submission_number is not None:
            submissions_left_task = \
                task.max_submission_number - len(submissions)

        submissions_left = submissions_left_contest
        if submissions_left_task is not None and \
            (submissions_left_contest is None or
             submissions_left_contest > submissions_left_task):
            submissions_left = submissions_left_task

        # Make sure we do not show negative value if admins changed
        # the maximum
        if submissions_left is not None:
            submissions_left = max(0, submissions_left)

        tokens_info = tokens_available(participation, task, self.timestamp)

        download_allowed = self.contest.submissions_download_allowed

        rparams['task'] = task
        rparams['submissions'] = submissions
        rparams['public_score'] = public_score
        rparams['tokened_score'] = tokened_score
        rparams['is_score_partial'] = is_score_partial
        rparams['tokens_task'] = task.token_mode
        rparams['tokens_info'] = tokens_info
        rparams['submissions_left'] = submissions_left
        rparams['submissions_download_allowed'] = download_allowed

        return rparams
Exemple #17
0
 def call(self, public=False, only_tokened=False, rounded=False):
     return task_score(self.participation,
                       self.task,
                       public=public,
                       only_tokened=only_tokened,
                       rounded=rounded)
Exemple #18
0
 def call(self):
     return task_score(self.participation, self.task)