Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bb17b1a
Add a Score counter to task overview
pxsit Jul 23, 2025
6653f15
Merge branch 'main' into add-counter
pxsit Jul 23, 2025
3d62b5d
Merge branch 'main' into add-counter
pxsit Jul 23, 2025
6b52ff7
Merge branch 'main' into add-counter
pxsit Jul 28, 2025
8428046
Merge branch 'main' into add-counter
pxsit Aug 1, 2025
4ae18e9
Restore Formatting, Make it toggleable
pxsit Aug 1, 2025
cbe5f8e
Fix color not displaying
pxsit Aug 1, 2025
8055bf6
Fix
pxsit Aug 3, 2025
fc50113
Fix the Fix
pxsit Aug 4, 2025
251c8b5
Fix the Fix that Fix the Fix
pxsit Aug 4, 2025
527bdea
Merge branch 'main' into add-counter
pxsit Aug 5, 2025
95cc100
Update cws_style.css
pxsit Aug 5, 2025
e60f328
Merge branch 'main' into add-counter
pxsit Aug 5, 2025
13c491e
Add credit for original owner?
pxsit Aug 5, 2025
95349b0
Merge branch 'main' into add-counter
pxsit Aug 6, 2025
28c1ae1
Merge branch 'main' into add-counter
pxsit Aug 6, 2025
e0840f6
Update update_from_1.5.sql
pxsit Aug 6, 2025
a958b48
Merge branch 'main' into add-counter
pxsit Aug 8, 2025
7a3b7b7
Merge branch 'main' into add-counter
pxsit Aug 11, 2025
fa4e7ca
Merge branch 'main' into add-counter
pxsit Aug 15, 2025
4b0bd83
Merge branch 'main' into add-counter
pxsit Aug 16, 2025
d12ba11
Merge branch 'cms-dev:main' into add-counter
pxsit Sep 21, 2025
85d0eb8
Merge branch 'cms-dev:main' into add-counter
pxsit Sep 30, 2025
3181514
Merge branch 'cms-dev:main' into add-counter
pxsit Oct 8, 2025
003a7f9
Merge branch 'cms-dev:main' into add-counter
pxsit Oct 23, 2025
7aac12b
Remove unused PrintJob import from main.py
pxsit Nov 17, 2025
1134711
Merge branch 'main' into add-counter
pxsit Nov 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cms/db/contest.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ class Contest(Base):
nullable=False,
default=False)

# Whether to show task scores in the overview page
show_task_scores_in_overview: bool = Column(Boolean, nullable=False, default=True)

# Whether to prevent hidden participations to log in.
block_hidden_participations: bool = Column(
Boolean,
Expand Down
1 change: 1 addition & 0 deletions cms/server/admin/handlers/contest.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def post(self, contest_id: str):
self.get_bool(attrs, "allow_questions")
self.get_bool(attrs, "allow_user_tests")
self.get_bool(attrs, "allow_unofficial_submission_before_analysis_mode")
self.get_bool(attrs, "show_task_scores_in_overview")
self.get_bool(attrs, "block_hidden_participations")
self.get_bool(attrs, "allow_password_authentication")
self.get_bool(attrs, "allow_registration")
Expand Down
9 changes: 9 additions & 0 deletions cms/server/admin/templates/contest.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ <h1>Contest configuration</h1>
<input type="checkbox" id="allow_unofficial_submission_before_analysis_mode" name="allow_unofficial_submission_before_analysis_mode" {{ "checked" if contest.allow_unofficial_submission_before_analysis_mode else "" }}/>
</td>
</tr>
<tr>
<td>
<span class="info" title="Whether to show task scores in the overview page for contestants."></span>
<label for="show_task_scores_in_overview">Show task scores in overview page</label>
</td>
<td>
<input type="checkbox" id="show_task_scores_in_overview" name="show_task_scores_in_overview" {{ "checked" if contest.show_task_scores_in_overview else "" }}/>
</td>
</tr>

<tr><td colspan=2><h2>Logging in</h2></td></tr>
<tr>
Expand Down
59 changes: 58 additions & 1 deletion cms/server/contest/handlers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# Copyright © 2014 Fabian Gundlach <[email protected]>
# Copyright © 2015-2018 William Di Luigi <[email protected]>
# Copyright © 2021 Grace Hawkins <[email protected]>
# Copyright © 2025 Pasit Sangprachathanarak <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
Expand Down Expand Up @@ -46,11 +47,13 @@

import tornado.web
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy.orm import joinedload

from cms import config
from cms.db import PrintJob, User, Participation, Team
from cms.db import PrintJob, User, Participation, Team, Submission, Token, Task, Dataset
from cms.grading.languagemanager import get_language
from cms.grading.steps import COMPILATION_MESSAGES, EVALUATION_MESSAGES
from cms.grading.scoring import task_score
from cms.server import multi_contest
from cms.server.contest.authentication import validate_login
from cms.server.contest.communication import get_communications
Expand All @@ -76,8 +79,62 @@ class MainHandler(ContestHandler):
"""
@multi_contest
def get(self):
self.r_params = self.render_params()
self.render("overview.html", **self.r_params)

def render_params(self):
ret = super().render_params()

if self.current_user is not None:
# This massive joined load gets all the information which we will need
participation = (
self.sql_session.query(Participation)
.filter(Participation.id == self.current_user.id)
.options(
joinedload(Participation.user),
joinedload(Participation.contest),
joinedload(Participation.submissions).joinedload(Submission.token),
joinedload(Participation.submissions).joinedload(
Submission.results
),
)
.first()
)

self.contest = (
self.sql_session.query(Contest)
.filter(Contest.id == participation.contest.id)
.options(joinedload(Contest.tasks).joinedload(Task.active_dataset))
.first()
)

ret["participation"] = participation

# Compute public scores for all tasks only if they will be shown
if self.contest.show_task_scores_in_overview:
task_scores = {}
for task in self.contest.tasks:
score_type = task.active_dataset.score_type_object
max_public_score = round(
score_type.max_public_score, task.score_precision
)
public_score, _ = task_score(
participation, task, public=True, rounded=True
)
task_scores[task.id] = (
public_score,
max_public_score,
score_type.format_score(
public_score,
score_type.max_public_score,
None,
task.score_precision,
translation=self.translation,
),
)
ret["task_scores"] = task_scores

return ret

class RegistrationHandler(ContestHandler):
"""Registration handler.
Expand Down
18 changes: 12 additions & 6 deletions cms/server/contest/static/cws_style.css
Original file line number Diff line number Diff line change
Expand Up @@ -559,27 +559,33 @@ td.token_rules p:last-child {
color: #AAA;
}

.submission_list td.public_score.score_0 {
.submission_list td.public_score.score_0,
.main_task_list td.public_score.score_0 {
background-color: hsla(0, 100%, 50%, 0.4);
}

.submission_list tr:hover td.public_score.score_0 {
.submission_list tr:hover td.public_score.score_0,
.main_task_list tr:hover td.public_score.score_0 {
background-color: hsla(0, 100%, 50%, 0.5);
}

.submission_list td.public_score.score_0_100 {
.submission_list td.public_score.score_0_100,
.main_task_list td.public_score.score_0_100 {
background-color: hsla(60, 100%, 50%, 0.4);
}

.submission_list tr:hover td.public_score.score_0_100 {
.submission_list tr:hover td.public_score.score_0_100,
.main_task_list tr:hover td.public_score.score_0_100 {
background-color: hsla(60, 100%, 50%, 0.5);
}

.submission_list td.public_score.score_100 {
.submission_list td.public_score.score_100,
.main_task_list td.public_score.score_100 {
background-color: hsla(120, 100%, 50%, 0.4);
}

.submission_list tr:hover td.public_score.score_100 {
.submission_list tr:hover td.public_score.score_100,
.main_task_list tr:hover td.public_score.score_100 {
background-color: hsla(120, 100%, 50%, 0.5);
}

Expand Down
8 changes: 7 additions & 1 deletion cms/server/contest/templates/overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ <h2>{% trans %}General information{% endtrans %}</h2>
{% if actual_phase == 0 or actual_phase == 3 or participation.unrestricted or (0 <= actual_phase <= 3 and contest.allow_unofficial_submission_before_analysis_mode)%}
<h2>{% trans %}Task overview{% endtrans %}</h2>

<table class="table table-bordered table-striped">
<table class="main_task_list table table-bordered table-striped">
<!-- <colgroup>
<col class="task"/>
<col class="time_limit"/>
Expand All @@ -193,6 +193,9 @@ <h2>{% trans %}Task overview{% endtrans %}</h2>
</colgroup> -->
<thead>
<tr>
{% if contest.show_task_scores_in_overview %}
<th>{% trans %}Score{% endtrans %}</th>
{% endif %}
<th>{% trans %}Task{% endtrans %}</th>
<th>{% trans %}Name{% endtrans %}</th>
<th>{% trans %}Time limit{% endtrans %}</th>
Expand All @@ -208,6 +211,9 @@ <h2>{% trans %}Task overview{% endtrans %}</h2>
{% set extensions = "[%s]"|format(contest.languages|map("to_language")|map(attribute="source_extension")|unique|join("|")) %}
{% for t_iter in contest.tasks %}
<tr>
{% if contest.show_task_scores_in_overview and task_scores is defined %}
<td class="public_score {{ get_score_class(task_scores[t_iter.id][0], task_scores[t_iter.id][1], t_iter.score_precision) }}">{{ task_scores[t_iter.id][2] }}</td>
{% endif %}
<th>{{ t_iter.name }}</th>
<td>{{ t_iter.title }}</td>
<td>
Expand Down
4 changes: 4 additions & 0 deletions cmscontrib/updaters/update_from_1.5.sql
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ ALTER TABLE user_test_results ADD COLUMN evaluation_sandbox_digests VARCHAR[];
UPDATE user_test_results SET evaluation_sandbox_paths = string_to_array(evaluation_sandbox, ':');
ALTER TABLE user_test_results DROP COLUMN evaluation_sandbox;

-- https://github.com/cms-dev/cms/pull/1476
ALTER TABLE contests ADD COLUMN show_task_scores_in_overview boolean NOT NULL DEFAULT true;
ALTER TABLE contests ALTER COLUMN show_task_scores_in_overview DROP DEFAULT;

COMMIT;