Add contest freeze

This commit is contained in:
cuom1999 2022-11-18 16:59:58 -06:00
parent 2c39774ff7
commit a35ea0f6d5
13 changed files with 338 additions and 215 deletions

View file

@ -158,7 +158,10 @@ class ContestAdmin(CompareVersionAdmin):
)
},
),
(_("Scheduling"), {"fields": ("start_time", "end_time", "time_limit")}),
(
_("Scheduling"),
{"fields": ("start_time", "end_time", "time_limit", "freeze_after")},
),
(
_("Details"),
{
@ -274,7 +277,8 @@ class ContestAdmin(CompareVersionAdmin):
# We need this flag because `save_related` deals with the inlines, but does not know if we have already rescored
self._rescored = False
if form.changed_data and any(
f in form.changed_data for f in ("format_config", "format_name")
f in form.changed_data
for f in ("format_config", "format_name", "freeze_after")
):
self._rescore(obj.key)
self._rescored = True

View file

@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy
from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.timezone import from_database_time, to_database_time
from judge.utils.timedelta import nice_repr
@ -54,6 +54,10 @@ class AtCoderContestFormat(DefaultContestFormat):
points = 0
format_data = {}
frozen_time = self.contest.end_time
if self.contest.freeze_after:
frozen_time = participation.start + self.contest.freeze_after
with connection.cursor() as cursor:
cursor.execute(
"""
@ -66,9 +70,10 @@ class AtCoderContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id
""",
(participation.id, participation.id),
(participation.id, participation.id, to_database_time(frozen_time)),
)
for score, time, prob in cursor.fetchall():
@ -100,6 +105,7 @@ class AtCoderContestFormat(DefaultContestFormat):
format_data[str(prob)] = {"time": dt, "points": score, "penalty": prev}
points += score
self.handle_frozen_state(participation, format_data)
participation.cumtime = cumtime + penalty
participation.score = points
participation.tiebreaker = 0
@ -114,7 +120,7 @@ class AtCoderContestFormat(DefaultContestFormat):
'<small style="color:red"> ({penalty})</small>',
penalty=floatformat(format_data["penalty"]),
)
if format_data["penalty"]
if format_data.get("penalty")
else ""
)
return format_html(
@ -129,6 +135,7 @@ class AtCoderContestFormat(DefaultContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -1,4 +1,5 @@
from abc import ABCMeta, abstractmethod, abstractproperty
from django.db.models import Max
class abstractclassmethod(classmethod):
@ -95,3 +96,17 @@ class BaseContestFormat(metaclass=ABCMeta):
if points == total:
return "full-score"
return "partial-score"
def handle_frozen_state(self, participation, format_data):
if not self.contest.freeze_after:
return
queryset = participation.submissions.values("problem_id").annotate(
time=Max("submission__date")
)
for result in queryset:
problem = str(result["problem_id"])
if format_data.get(problem):
if result["time"] >= self.contest.freeze_after + participation.start:
format_data[problem]["frozen"] = True
else:
format_data[problem] = {"time": 0, "points": 0, "frozen": True}

View file

@ -32,10 +32,19 @@ class DefaultContestFormat(BaseContestFormat):
points = 0
format_data = {}
for result in participation.submissions.values("problem_id").annotate(
queryset = participation.submissions
if self.contest.freeze_after:
queryset = queryset.filter(
submission__date__lt=participation.start + self.contest.freeze_after
)
queryset = queryset.values("problem_id").annotate(
time=Max("submission__date"),
points=Max("points"),
):
)
for result in queryset:
dt = (result["time"] - participation.start).total_seconds()
if result["points"]:
cumtime += dt
@ -45,6 +54,7 @@ class DefaultContestFormat(BaseContestFormat):
}
points += result["points"]
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(cumtime, 0)
participation.score = points
participation.tiebreaker = 0
@ -66,6 +76,7 @@ class DefaultContestFormat(BaseContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy
from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.timezone import from_database_time, to_database_time
from judge.utils.timedelta import nice_repr
@ -60,6 +60,10 @@ class ECOOContestFormat(DefaultContestFormat):
points = 0
format_data = {}
frozen_time = self.contest.end_time
if self.contest.freeze_after:
frozen_time = participation.start + self.contest.freeze_after
with connection.cursor() as cursor:
cursor.execute(
"""
@ -77,9 +81,15 @@ class ECOOContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id
""",
(participation.id, participation.id, participation.id),
(
participation.id,
participation.id,
participation.id,
to_database_time(frozen_time),
),
)
for score, time, prob, subs, max_score in cursor.fetchall():
@ -105,6 +115,7 @@ class ECOOContestFormat(DefaultContestFormat):
format_data[str(prob)] = {"time": dt, "points": score, "bonus": bonus}
points += score
self.handle_frozen_state(participation, format_data)
participation.cumtime = cumtime
participation.score = points
participation.tiebreaker = 0
@ -134,6 +145,7 @@ class ECOOContestFormat(DefaultContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -10,7 +10,7 @@ from django.utils.translation import gettext_lazy
from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.timezone import from_database_time, to_database_time
from judge.utils.timedelta import nice_repr
@ -55,6 +55,10 @@ class ICPCContestFormat(DefaultContestFormat):
score = 0
format_data = {}
frozen_time = self.contest.end_time
if self.contest.freeze_after:
frozen_time = participation.start + self.contest.freeze_after
with connection.cursor() as cursor:
cursor.execute(
"""
@ -67,9 +71,10 @@ class ICPCContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id
""",
(participation.id, participation.id),
(participation.id, participation.id, to_database_time(frozen_time)),
)
for points, time, prob in cursor.fetchall():
@ -102,6 +107,7 @@ class ICPCContestFormat(DefaultContestFormat):
format_data[str(prob)] = {"time": dt, "points": points, "penalty": prev}
score += points
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(0, cumtime + penalty)
participation.score = score
participation.tiebreaker = last # field is sorted from least to greatest
@ -116,7 +122,7 @@ class ICPCContestFormat(DefaultContestFormat):
'<small style="color:red"> +{penalty}</small>',
penalty=floatformat(format_data["penalty"]),
)
if format_data["penalty"]
if format_data.get("penalty")
else ""
)
return format_html(
@ -131,6 +137,7 @@ class ICPCContestFormat(DefaultContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -12,6 +12,7 @@ from judge.contest_format.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format
from judge.timezone import from_database_time
from judge.utils.timedelta import nice_repr
from django.db.models import Min, OuterRef, Subquery
@register_contest_format("ioi")
@ -45,41 +46,42 @@ class IOIContestFormat(DefaultContestFormat):
def update_participation(self, participation):
cumtime = 0
points = 0
score = 0
format_data = {}
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT MAX(cs.points) as `score`, (
SELECT MIN(csub.date)
FROM judge_contestsubmission ccs LEFT OUTER JOIN
judge_submission csub ON (csub.id = ccs.submission_id)
WHERE ccs.problem_id = cp.id AND ccs.participation_id = %s AND ccs.points = MAX(cs.points)
) AS `time`, cp.id AS `prob`
FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER JOIN
judge_submission sub ON (sub.id = cs.submission_id)
GROUP BY cp.id
""",
(participation.id, participation.id),
queryset = participation.submissions
if self.contest.freeze_after:
queryset = queryset.filter(
submission__date__lt=participation.start + self.contest.freeze_after
)
for score, time, prob in cursor.fetchall():
queryset = (
queryset.values("problem_id")
.filter(
points=Subquery(
queryset.filter(problem_id=OuterRef("problem_id"))
.order_by("-points")
.values("points")[:1]
)
)
.annotate(time=Min("submission__date"))
.values_list("problem_id", "time", "points")
)
for problem_id, time, points in queryset:
if self.config["cumtime"]:
dt = (
from_database_time(time) - participation.start
).total_seconds()
if score:
dt = (time - participation.start).total_seconds()
if points:
cumtime += dt
else:
dt = 0
format_data[str(prob)] = {"time": dt, "points": score}
points += score
format_data[str(problem_id)] = {"points": points, "time": dt}
score += points
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(cumtime, 0)
participation.score = points
participation.score = score
participation.tiebreaker = 0
participation.format_data = format_data
participation.save()
@ -99,6 +101,7 @@ class IOIContestFormat(DefaultContestFormat):
+ self.best_solution_state(
format_data["points"], contest_problem.points
)
+ (" frozen" if format_data.get("frozen") else "")
),
url=reverse(
"contest_user_submissions_ajax",

View file

@ -0,0 +1,23 @@
# Generated by Django 3.2.16 on 2022-11-18 06:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("judge", "0138_bookmark_makebookmark"),
]
operations = [
migrations.AddField(
model_name="contest",
name="freeze_after",
field=models.DurationField(
blank=True,
help_text="Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, enter 02:00:00",
null=True,
verbose_name="freeze after",
),
),
]

View file

@ -128,6 +128,14 @@ class Contest(models.Model):
"Format hh:mm:ss. For example, if you want a 2-hour contest, enter 02:00:00"
),
)
freeze_after = models.DurationField(
verbose_name=_("freeze after"),
blank=True,
null=True,
help_text=_(
"Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, enter 02:00:00"
),
)
is_visible = models.BooleanField(
verbose_name=_("publicly visible"),
default=False,

View file

@ -2,7 +2,7 @@ import pytz
from django.conf import settings
from django.db import connection
from django.utils import timezone
from django.utils.timezone import make_aware
from django.utils.timezone import make_aware, make_naive
class TimezoneMiddleware(object):
@ -25,3 +25,10 @@ def from_database_time(datetime):
if tz is None:
return datetime
return make_aware(datetime, tz)
def to_database_time(datetime):
tz = connection.timezone
if tz is None:
return datetime
return make_naive(datetime, tz)

View file

@ -301,6 +301,7 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
template_name = "submission/list.html"
context_object_name = "submissions"
first_page_href = None
include_frozen = False
def get_result_data(self):
result = self._get_result_data()
@ -344,6 +345,10 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
queryset = queryset.filter(contest_object=self.contest)
if not self.contest.can_see_full_scoreboard(self.request.user):
queryset = queryset.filter(user=self.request.profile)
if self.contest.freeze_after and not self.include_frozen:
queryset = queryset.exclude(
date__gte=self.contest.freeze_after + self.contest.start_time
)
else:
queryset = queryset.select_related("contest_object").defer(
"contest_object__description"
@ -456,6 +461,9 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
self.selected_languages = set(request.GET.getlist("language"))
self.selected_statuses = set(request.GET.getlist("status"))
if request.user.is_superuser:
self.include_frozen = True
if "results" in request.GET:
return JsonResponse(self.get_result_data())
@ -468,6 +476,8 @@ class UserMixin(object):
raise ImproperlyConfigured("Must pass a user")
self.profile = get_object_or_404(Profile, user__username=kwargs["user"])
self.username = kwargs["user"]
if self.profile == request.profile:
self.include_frozen = True
return super(UserMixin, self).get(request, *args, **kwargs)
@ -837,8 +847,8 @@ class UserContestSubmissionsAjax(UserContestSubmissions):
s.contest_time = nice_repr(contest_time, "noday")
else:
s.contest_time = None
points = floatformat(s.contest.points, -self.contest.points_precision)
total = floatformat(contest_problem.points, -self.contest.points_precision)
points = floatformat(s.contest.points, -self.contest.points_precision)
s.display_point = f"{points} / {total}"
filtered_submissions.append(s)
context["submissions"] = filtered_submissions

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: lqdoj2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-18 05:10+0700\n"
"POT-Creation-Date: 2022-11-19 03:12+0700\n"
"PO-Revision-Date: 2021-07-20 03:44\n"
"Last-Translator: Icyene\n"
"Language-Team: Vietnamese\n"
@ -19,8 +19,8 @@ msgstr ""
"X-Crowdin-File-ID: 5\n"
#: chat_box/models.py:31 chat_box/models.py:51 chat_box/models.py:65
#: judge/admin/interface.py:150 judge/models/contest.py:622
#: judge/models/contest.py:815 judge/models/profile.py:354
#: judge/admin/interface.py:150 judge/models/contest.py:630
#: judge/models/contest.py:823 judge/models/profile.py:354
#: judge/models/profile.py:430
msgid "user"
msgstr "người dùng"
@ -99,72 +99,72 @@ msgstr "Bài tập"
msgid "Settings"
msgstr "Cài đặt"
#: judge/admin/contest.py:161
#: judge/admin/contest.py:162
msgid "Scheduling"
msgstr ""
#: judge/admin/contest.py:163
#: judge/admin/contest.py:166
msgid "Details"
msgstr "Chi tiết"
#: judge/admin/contest.py:175
#: judge/admin/contest.py:178
msgid "Format"
msgstr "Thể thức"
#: judge/admin/contest.py:179 templates/contest/ranking-table.html:7
#: judge/admin/contest.py:182 templates/contest/ranking-table.html:7
#: templates/user/user-about.html:15 templates/user/user-about.html:45
msgid "Rating"
msgstr ""
#: judge/admin/contest.py:191
#: judge/admin/contest.py:194
msgid "Access"
msgstr "Truy cập"
#: judge/admin/contest.py:201 judge/admin/problem.py:219
#: judge/admin/contest.py:204 judge/admin/problem.py:219
msgid "Justice"
msgstr "Xử phạt"
#: judge/admin/contest.py:313
#: judge/admin/contest.py:317
#, python-format
msgid "%d contest successfully marked as visible."
msgid_plural "%d contests successfully marked as visible."
msgstr[0] "%d kỳ thi đã được đánh dấu hiển thị."
#: judge/admin/contest.py:320
#: judge/admin/contest.py:324
msgid "Mark contests as visible"
msgstr "Đánh dấu hiển thị các kỳ thi"
#: judge/admin/contest.py:331
#: judge/admin/contest.py:335
#, python-format
msgid "%d contest successfully marked as hidden."
msgid_plural "%d contests successfully marked as hidden."
msgstr[0] "%d kỳ thi đã được đánh dấu ẩn."
#: judge/admin/contest.py:338
#: judge/admin/contest.py:342
msgid "Mark contests as hidden"
msgstr "Ẩn các kỳ thi"
#: judge/admin/contest.py:359 judge/admin/submission.py:243
#: judge/admin/contest.py:363 judge/admin/submission.py:243
#, python-format
msgid "%d submission was successfully scheduled for rejudging."
msgid_plural "%d submissions were successfully scheduled for rejudging."
msgstr[0] "%d bài nộp đã được lên lịch thành công để chấm lại."
#: judge/admin/contest.py:467
#: judge/admin/contest.py:471
#, python-format
msgid "%d participation recalculated."
msgid_plural "%d participations recalculated."
msgstr[0] "%d thí sinh đã được tính điểm lại."
#: judge/admin/contest.py:474
#: judge/admin/contest.py:478
msgid "Recalculate results"
msgstr "Tính toán lại kết quả"
#: judge/admin/contest.py:479 judge/admin/organization.py:99
#: judge/admin/contest.py:483 judge/admin/organization.py:99
msgid "username"
msgstr "tên đăng nhập"
#: judge/admin/contest.py:485 templates/base.html:315
#: judge/admin/contest.py:489 templates/base.html:315
msgid "virtual"
msgstr "ảo"
@ -567,13 +567,13 @@ msgstr "Lưu"
msgid "bookmarks"
msgstr "Lưu"
#: judge/models/bookmark.py:56
#: judge/models/bookmark.py:59
#, fuzzy
#| msgid "Bookmark"
msgid "make bookmark"
msgstr "Lưu"
#: judge/models/bookmark.py:57
#: judge/models/bookmark.py:60
#, fuzzy
#| msgid "Bookmark"
msgid "make bookmarks"
@ -696,7 +696,7 @@ msgstr ""
msgid "contest tag"
msgstr ""
#: judge/models/contest.py:74 judge/models/contest.py:234
#: judge/models/contest.py:74 judge/models/contest.py:242
msgid "contest tags"
msgstr "nhãn kỳ thi"
@ -751,7 +751,7 @@ msgstr "mô tả"
msgid "problems"
msgstr "bài tập"
#: judge/models/contest.py:121 judge/models/contest.py:627
#: judge/models/contest.py:121 judge/models/contest.py:635
msgid "start time"
msgstr "thời gian bắt đầu"
@ -771,11 +771,23 @@ msgstr ""
"Định dạng hh:mm:ss (giờ:phút:giây). Ví dụ, nếu muốn tạo kỳ thi dài 2h, hãy "
"nhập 02:00:00"
#: judge/models/contest.py:132 judge/models/problem.py:222
#: judge/models/contest.py:132
msgid "freeze after"
msgstr "đóng băng sau"
#: judge/models/contest.py:136
msgid ""
"Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, "
"enter 02:00:00"
msgstr ""
"Định dạng hh:mm:ss (giờ:phút:giây). Ví dụ, nếu muốn đóng băng kỳ thi sau 2h, hãy "
"nhập 02:00:00"
#: judge/models/contest.py:140 judge/models/problem.py:222
msgid "publicly visible"
msgstr "công khai"
#: judge/models/contest.py:135
#: judge/models/contest.py:143
msgid ""
"Should be set even for organization-private contests, where it determines "
"whether the contest is visible to members of the specified organizations."
@ -783,84 +795,84 @@ msgstr ""
"Đánh dấu ngay cả với các kỳ thi riêng tư của nhóm, quyết định việc kỳ thi có "
"được hiển thị với các thành viên hay không."
#: judge/models/contest.py:141
#: judge/models/contest.py:149
msgid "contest rated"
msgstr "kỳ thi được xếp hạng"
#: judge/models/contest.py:142
#: judge/models/contest.py:150
msgid "Whether this contest can be rated."
msgstr "Quyết định kỳ thi có được xếp hạng không."
#: judge/models/contest.py:146
#: judge/models/contest.py:154
msgid "scoreboard visibility"
msgstr "khả năng hiển thị của bảng điểm"
#: judge/models/contest.py:149
#: judge/models/contest.py:157
msgid "Scoreboard visibility through the duration of the contest"
msgstr "Khả năng hiển thị của bảng điểm trong thời gian kỳ thi"
#: judge/models/contest.py:154
#: judge/models/contest.py:162
msgid "view contest scoreboard"
msgstr "xem bảng điểm kỳ thi"
#: judge/models/contest.py:157
#: judge/models/contest.py:165
msgid "These users will be able to view the scoreboard."
msgstr "Những người dùng này được phép xem bảng điểm."
#: judge/models/contest.py:160
#: judge/models/contest.py:168
msgid "no comments"
msgstr "không bình luận"
#: judge/models/contest.py:161
#: judge/models/contest.py:169
msgid "Use clarification system instead of comments."
msgstr "Dùng hệ thống thông báo thay vì bình luận."
#: judge/models/contest.py:166
#: judge/models/contest.py:174
msgid "Rating floor for contest"
msgstr "Cận dưới rating được xếp hạng trong kỳ thi"
#: judge/models/contest.py:172
#: judge/models/contest.py:180
msgid "Rating ceiling for contest"
msgstr "Cận trên rating được xếp hạng trong kỳ thi"
#: judge/models/contest.py:177
#: judge/models/contest.py:185
msgid "rate all"
msgstr "xếp hạng tất cả"
#: judge/models/contest.py:178
#: judge/models/contest.py:186
msgid "Rate all users who joined."
msgstr "Xếp hạng tất cả người dùng đã tham gia (kể cả không nộp)."
#: judge/models/contest.py:183
#: judge/models/contest.py:191
msgid "exclude from ratings"
msgstr "không xếp hạng"
#: judge/models/contest.py:188
#: judge/models/contest.py:196
msgid "private to specific users"
msgstr "riêng tư với các người dùng này"
#: judge/models/contest.py:193
#: judge/models/contest.py:201
msgid "private contestants"
msgstr "thí sinh riêng tư"
#: judge/models/contest.py:194
#: judge/models/contest.py:202
msgid "If private, only these users may see the contest"
msgstr "Nếu riêng tư, chỉ những người dùng này mới thấy kỳ thi"
#: judge/models/contest.py:198
#: judge/models/contest.py:206
msgid "hide problem tags"
msgstr "ẩn nhãn kỳ thi"
#: judge/models/contest.py:199
#: judge/models/contest.py:207
msgid "Whether problem tags should be hidden by default."
msgstr ""
"Quyết định việc nhãn bài tập (DP, Tham lam, ...) được ẩn trong kỳ thi không."
#: judge/models/contest.py:203
#: judge/models/contest.py:211
msgid "run pretests only"
msgstr "chỉ chạy pretests"
#: judge/models/contest.py:205
#: judge/models/contest.py:213
msgid ""
"Whether judges should grade pretests only, versus all testcases. Commonly "
"set during a contest, then unset prior to rejudging user submissions when "
@ -869,50 +881,50 @@ msgstr ""
"Quyết định việc các máy chấm chỉ chấm pretests thay vì tất cả các test. Sau "
"kỳ thi, hãy bỏ đánh dấu ô này và chấm lại tất cả các bài."
#: judge/models/contest.py:212 judge/models/interface.py:92
#: judge/models/contest.py:220 judge/models/interface.py:92
#: judge/models/problem.py:279
msgid "private to organizations"
msgstr "riêng tư với các tổ chức"
#: judge/models/contest.py:217 judge/models/interface.py:88
#: judge/models/contest.py:225 judge/models/interface.py:88
#: judge/models/problem.py:275 judge/models/profile.py:126
msgid "organizations"
msgstr "tổ chức"
#: judge/models/contest.py:218
#: judge/models/contest.py:226
msgid "If private, only these organizations may see the contest"
msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được kỳ thi"
#: judge/models/contest.py:221 judge/models/problem.py:253
#: judge/models/contest.py:229 judge/models/problem.py:253
msgid "OpenGraph image"
msgstr "Hình ảnh OpenGraph"
#: judge/models/contest.py:224 judge/models/profile.py:81
#: judge/models/contest.py:232 judge/models/profile.py:81
msgid "Logo override image"
msgstr "Hình ảnh ghi đè logo"
#: judge/models/contest.py:229
#: judge/models/contest.py:237
msgid ""
"This image will replace the default site logo for users inside the contest."
msgstr "Ảnh này sẽ thay thế cho logo mặc định trong kỳ thi."
#: judge/models/contest.py:237
#: judge/models/contest.py:245
msgid "the amount of live participants"
msgstr "số lượng thí sinh thi trực tiếp"
#: judge/models/contest.py:241
#: judge/models/contest.py:249
msgid "contest summary"
msgstr "tổng kết kỳ thi"
#: judge/models/contest.py:243 judge/models/problem.py:259
#: judge/models/contest.py:251 judge/models/problem.py:259
msgid "Plain-text, shown in meta description tag, e.g. for social media."
msgstr ""
#: judge/models/contest.py:247 judge/models/profile.py:76
#: judge/models/contest.py:255 judge/models/profile.py:76
msgid "access code"
msgstr "mật khẩu truy cập"
#: judge/models/contest.py:252
#: judge/models/contest.py:260
msgid ""
"An optional code to prompt contestants before they are allowed to join the "
"contest. Leave it blank to disable."
@ -920,267 +932,267 @@ msgstr ""
"Mật khẩu truy cập cho các thí sinh muốn tham gia kỳ thi. Để trống nếu không "
"dùng."
#: judge/models/contest.py:258 judge/models/problem.py:241
#: judge/models/contest.py:266 judge/models/problem.py:241
msgid "personae non gratae"
msgstr "Chặn tham gia"
#: judge/models/contest.py:260
#: judge/models/contest.py:268
msgid "Bans the selected users from joining this contest."
msgstr "Cấm những người dùng được chọn tham gia kỳ thi."
#: judge/models/contest.py:263
#: judge/models/contest.py:271
msgid "contest format"
msgstr "format kỳ thi"
#: judge/models/contest.py:267
#: judge/models/contest.py:275
msgid "The contest format module to use."
msgstr "Format kỳ thi sử dụng."
#: judge/models/contest.py:270
#: judge/models/contest.py:278
msgid "contest format configuration"
msgstr "Tùy chỉnh format kỳ thi"
#: judge/models/contest.py:274
#: judge/models/contest.py:282
msgid ""
"A JSON object to serve as the configuration for the chosen contest format "
"module. Leave empty to use None. Exact format depends on the contest format "
"selected."
msgstr ""
#: judge/models/contest.py:287
#: judge/models/contest.py:295
msgid "precision points"
msgstr "Hiển thị điểm"
#: judge/models/contest.py:290
#: judge/models/contest.py:298
msgid "Number of digits to round points to."
msgstr "Số chữ số thập phân trên bảng điểm."
#: judge/models/contest.py:595
#: judge/models/contest.py:603
msgid "See private contests"
msgstr ""
#: judge/models/contest.py:596
#: judge/models/contest.py:604
msgid "Edit own contests"
msgstr ""
#: judge/models/contest.py:597
#: judge/models/contest.py:605
msgid "Edit all contests"
msgstr ""
#: judge/models/contest.py:598
#: judge/models/contest.py:606
msgid "Clone contest"
msgstr ""
#: judge/models/contest.py:599 templates/contest/moss.html:74
#: judge/models/contest.py:607 templates/contest/moss.html:74
msgid "MOSS contest"
msgstr ""
#: judge/models/contest.py:600
#: judge/models/contest.py:608
msgid "Rate contests"
msgstr ""
#: judge/models/contest.py:601
#: judge/models/contest.py:609
msgid "Contest access codes"
msgstr ""
#: judge/models/contest.py:602
#: judge/models/contest.py:610
msgid "Create private contests"
msgstr ""
#: judge/models/contest.py:603
#: judge/models/contest.py:611
msgid "Change contest visibility"
msgstr ""
#: judge/models/contest.py:604
#: judge/models/contest.py:612
msgid "Edit contest problem label script"
msgstr "Cách hiển thị thứ tự bài tập"
#: judge/models/contest.py:606 judge/models/contest.py:744
#: judge/models/contest.py:818 judge/models/contest.py:848
#: judge/models/contest.py:614 judge/models/contest.py:752
#: judge/models/contest.py:826 judge/models/contest.py:856
#: judge/models/submission.py:116
msgid "contest"
msgstr "kỳ thi"
#: judge/models/contest.py:607
#: judge/models/contest.py:615
msgid "contests"
msgstr "kỳ thi"
#: judge/models/contest.py:616
#: judge/models/contest.py:624
msgid "associated contest"
msgstr ""
#: judge/models/contest.py:629
#: judge/models/contest.py:637
msgid "score"
msgstr "điểm"
#: judge/models/contest.py:630
#: judge/models/contest.py:638
msgid "cumulative time"
msgstr "tổng thời gian"
#: judge/models/contest.py:632
#: judge/models/contest.py:640
msgid "is disqualified"
msgstr "đã bị loại"
#: judge/models/contest.py:634
#: judge/models/contest.py:642
msgid "Whether this participation is disqualified."
msgstr "Quyết định thí sinh có bị loại không."
#: judge/models/contest.py:636
#: judge/models/contest.py:644
msgid "tie-breaking field"
msgstr ""
#: judge/models/contest.py:638
#: judge/models/contest.py:646
msgid "virtual participation id"
msgstr "id lần tham gia ảo"
#: judge/models/contest.py:640
#: judge/models/contest.py:648
msgid "0 means non-virtual, otherwise the n-th virtual participation."
msgstr "0 nghĩa là tham gia chính thức, ngược lại là lần tham gia ảo thứ n."
#: judge/models/contest.py:643
#: judge/models/contest.py:651
msgid "contest format specific data"
msgstr ""
#: judge/models/contest.py:719
#: judge/models/contest.py:727
#, python-format
msgid "%s spectating in %s"
msgstr "%s đang theo dõi trong %s"
#: judge/models/contest.py:724
#: judge/models/contest.py:732
#, python-format
msgid "%s in %s, v%d"
msgstr "%s trong %s, v%d"
#: judge/models/contest.py:729
#: judge/models/contest.py:737
#, python-format
msgid "%s in %s"
msgstr "%s trong %s"
#: judge/models/contest.py:732
#: judge/models/contest.py:740
msgid "contest participation"
msgstr "lần tham gia kỳ thi"
#: judge/models/contest.py:733
#: judge/models/contest.py:741
msgid "contest participations"
msgstr "lần tham gia kỳ thi"
#: judge/models/contest.py:740 judge/models/contest.py:789
#: judge/models/contest.py:851 judge/models/problem.py:558
#: judge/models/contest.py:748 judge/models/contest.py:797
#: judge/models/contest.py:859 judge/models/problem.py:558
#: judge/models/problem.py:565 judge/models/problem.py:586
#: judge/models/problem.py:617 judge/models/problem_data.py:50
msgid "problem"
msgstr "bài tập"
#: judge/models/contest.py:748 judge/models/contest.py:801
#: judge/models/contest.py:756 judge/models/contest.py:809
#: judge/models/problem.py:206
msgid "points"
msgstr "điểm"
#: judge/models/contest.py:749
#: judge/models/contest.py:757
msgid "partial"
msgstr "thành phần"
#: judge/models/contest.py:750 judge/models/contest.py:803
#: judge/models/contest.py:758 judge/models/contest.py:811
msgid "is pretested"
msgstr "dùng pretest"
#: judge/models/contest.py:751 judge/models/interface.py:43
#: judge/models/contest.py:759 judge/models/interface.py:43
msgid "order"
msgstr "thứ tự"
#: judge/models/contest.py:753
#: judge/models/contest.py:761
msgid "0 to not show testcases, 1 to show"
msgstr "0 để ẩn test, 1 để hiện"
#: judge/models/contest.py:754
#: judge/models/contest.py:762
msgid "visible testcases"
msgstr "hiển thị test"
#: judge/models/contest.py:761
#: judge/models/contest.py:769
msgid "Maximum number of submissions for this problem, or 0 for no limit."
msgstr "Số lần nộp tối đa, đặt là 0 nếu không có giới hạn."
#: judge/models/contest.py:763
#: judge/models/contest.py:771
msgid "max submissions"
msgstr "số lần nộp tối đa"
#: judge/models/contest.py:766
#: judge/models/contest.py:774
msgid "Why include a problem you can't submit to?"
msgstr ""
#: judge/models/contest.py:776
#: judge/models/contest.py:784
msgid "contest problem"
msgstr "bài trong kỳ thi"
#: judge/models/contest.py:777
#: judge/models/contest.py:785
msgid "contest problems"
msgstr "bài trong kỳ thi"
#: judge/models/contest.py:783 judge/models/submission.py:233
#: judge/models/contest.py:791 judge/models/submission.py:233
msgid "submission"
msgstr "bài nộp"
#: judge/models/contest.py:796 judge/models/contest.py:822
#: judge/models/contest.py:804 judge/models/contest.py:830
msgid "participation"
msgstr "lần tham gia"
#: judge/models/contest.py:804
#: judge/models/contest.py:812
msgid "Whether this submission was ran only on pretests."
msgstr "Quyết định bài nộp chỉ được chạy trên pretest không."
#: judge/models/contest.py:809
#: judge/models/contest.py:817
msgid "contest submission"
msgstr "bài nộp kỳ thi"
#: judge/models/contest.py:810
#: judge/models/contest.py:818
msgid "contest submissions"
msgstr "bài nộp kỳ thi"
#: judge/models/contest.py:826
#: judge/models/contest.py:834
msgid "rank"
msgstr "rank"
#: judge/models/contest.py:827
#: judge/models/contest.py:835
msgid "rating"
msgstr "rating"
#: judge/models/contest.py:828
#: judge/models/contest.py:836
msgid "raw rating"
msgstr "rating thật"
#: judge/models/contest.py:829
#: judge/models/contest.py:837
msgid "contest performance"
msgstr ""
#: judge/models/contest.py:830
#: judge/models/contest.py:838
msgid "last rated"
msgstr "lần cuối được xếp hạng"
#: judge/models/contest.py:834
#: judge/models/contest.py:842
msgid "contest rating"
msgstr "rating kỳ thi"
#: judge/models/contest.py:835
#: judge/models/contest.py:843
msgid "contest ratings"
msgstr "rating kỳ thi"
#: judge/models/contest.py:859
#: judge/models/contest.py:867
msgid "contest moss result"
msgstr "kết quả MOSS kỳ thi"
#: judge/models/contest.py:860
#: judge/models/contest.py:868
msgid "contest moss results"
msgstr "kết quả MOSS kỳ thi"
#: judge/models/contest.py:865
#: judge/models/contest.py:873
msgid "clarified problem"
msgstr ""
#: judge/models/contest.py:867
#: judge/models/contest.py:875
msgid "clarification body"
msgstr ""
#: judge/models/contest.py:869
#: judge/models/contest.py:877
msgid "clarification timestamp"
msgstr ""
@ -2554,7 +2566,7 @@ msgid "Editing comment"
msgstr "Chỉnh sửa bình luận"
#: judge/views/contests.py:119 judge/views/contests.py:367
#: judge/views/contests.py:372 judge/views/contests.py:613
#: judge/views/contests.py:372 judge/views/contests.py:619
msgid "No such contest"
msgstr "Không có contest nào như vậy"
@ -2578,113 +2590,113 @@ msgstr "Không tìm thấy kỳ thi nào như vậy."
msgid "Access to contest \"%s\" denied"
msgstr "Truy cập tới kỳ thi \"%s\" bị từ chối"
#: judge/views/contests.py:418
#: judge/views/contests.py:424
msgid "Clone Contest"
msgstr "Nhân bản kỳ thi"
#: judge/views/contests.py:487
#: judge/views/contests.py:493
msgid "Contest not ongoing"
msgstr "Kỳ thi đang không diễn ra"
#: judge/views/contests.py:488
#: judge/views/contests.py:494
#, python-format
msgid "\"%s\" is not currently ongoing."
msgstr "\"%s\" kỳ thi đang không diễn ra."
#: judge/views/contests.py:495
#: judge/views/contests.py:501
msgid "Already in contest"
msgstr "Đã ở trong kỳ thi"
#: judge/views/contests.py:496
#: judge/views/contests.py:502
#, python-format
msgid "You are already in a contest: \"%s\"."
msgstr "Bạn đã ở trong kỳ thi: \"%s\"."
#: judge/views/contests.py:506
#: judge/views/contests.py:512
msgid "Banned from joining"
msgstr "Bị cấm tham gia"
#: judge/views/contests.py:508
#: judge/views/contests.py:514
msgid ""
"You have been declared persona non grata for this contest. You are "
"permanently barred from joining this contest."
msgstr "Bạn không được phép tham gia kỳ thi này."
#: judge/views/contests.py:597
#: judge/views/contests.py:603
#, python-format
msgid "Enter access code for \"%s\""
msgstr "Nhập mật khẩu truy cập cho \"%s\""
#: judge/views/contests.py:614
#: judge/views/contests.py:620
#, python-format
msgid "You are not in contest \"%s\"."
msgstr "Bạn không ở trong kỳ thi \"%s\"."
#: judge/views/contests.py:637
#: judge/views/contests.py:643
msgid "ContestCalendar requires integer year and month"
msgstr "Lịch thi yêu cầu giá trị cho năm và tháng là số nguyên"
#: judge/views/contests.py:695
#: judge/views/contests.py:701
#, python-format
msgid "Contests in %(month)s"
msgstr "Các kỳ thi trong %(month)s"
#: judge/views/contests.py:696
#: judge/views/contests.py:702
msgid "F Y"
msgstr "F Y"
#: judge/views/contests.py:756
#: judge/views/contests.py:762
#, python-format
msgid "%s Statistics"
msgstr "%s Thống kê"
#: judge/views/contests.py:1013
#: judge/views/contests.py:1019
#, python-format
msgid "%s Rankings"
msgstr "%s Bảng điểm"
#: judge/views/contests.py:1024
#: judge/views/contests.py:1030
msgid "???"
msgstr "???"
#: judge/views/contests.py:1040
#: judge/views/contests.py:1046
#, python-format
msgid "Your participation in %s"
msgstr "Lần tham gia trong %s"
#: judge/views/contests.py:1041
#: judge/views/contests.py:1047
#, python-format
msgid "%s's participation in %s"
msgstr "Lần tham gia của %s trong %s"
#: judge/views/contests.py:1055
#: judge/views/contests.py:1061
msgid "Live"
msgstr "Trực tiếp"
#: judge/views/contests.py:1074 templates/contest/contest-tabs.html:13
#: judge/views/contests.py:1080 templates/contest/contest-tabs.html:13
msgid "Participation"
msgstr "Lần tham gia"
#: judge/views/contests.py:1123
#: judge/views/contests.py:1129
#, python-format
msgid "%s MOSS Results"
msgstr "%s Kết quả MOSS"
#: judge/views/contests.py:1159
#: judge/views/contests.py:1165
#, python-format
msgid "Running MOSS for %s..."
msgstr "Đang chạy MOSS cho %s..."
#: judge/views/contests.py:1182
#: judge/views/contests.py:1188
#, python-format
msgid "Contest tag: %s"
msgstr "Nhãn kỳ thi: %s"
#: judge/views/contests.py:1197 judge/views/ticket.py:72
#: judge/views/contests.py:1203 judge/views/ticket.py:72
msgid "Issue description"
msgstr "Mô tả vấn đề"
#: judge/views/contests.py:1244
#: judge/views/contests.py:1250
#, python-format
msgid "New clarification for %s"
msgstr "Thông báo mới cho %s"
@ -2930,41 +2942,41 @@ msgstr "Hướng dẫn cho {0}"
msgid "Editorial for <a href=\"{1}\">{0}</a>"
msgstr "Hướng dẫn cho <a href=\"{1}\">{0}</a>"
#: judge/views/problem.py:443 templates/contest/contest.html:81
#: judge/views/problem.py:447 templates/contest/contest.html:81
#: templates/organization/org-left-sidebar.html:4
#: templates/user/user-about.html:28 templates/user/user-bookmarks.html:32
#: templates/user/user-tabs.html:5 templates/user/users-table.html:19
msgid "Problems"
msgstr "Bài tập"
#: judge/views/problem.py:823
#: judge/views/problem.py:827
msgid "Problem feed"
msgstr "Bài tập"
#: judge/views/problem.py:1061
#: judge/views/problem.py:1065
msgid "Banned from submitting"
msgstr "Bị cấm nộp bài"
#: judge/views/problem.py:1063
#: judge/views/problem.py:1067
msgid ""
"You have been declared persona non grata for this problem. You are "
"permanently barred from submitting this problem."
msgstr "Bạn đã bị cấm nộp bài này."
#: judge/views/problem.py:1086
#: judge/views/problem.py:1090
msgid "Too many submissions"
msgstr "Quá nhiều lần nộp"
#: judge/views/problem.py:1088
#: judge/views/problem.py:1092
msgid "You have exceeded the submission limit for this problem."
msgstr "Bạn đã vượt quá số lần nộp cho bài này."
#: judge/views/problem.py:1167 judge/views/problem.py:1172
#: judge/views/problem.py:1171 judge/views/problem.py:1176
#, python-format
msgid "Submit to %(problem)s"
msgstr "Nộp bài cho %(problem)s"
#: judge/views/problem.py:1195
#: judge/views/problem.py:1199
msgid "Clone Problem"
msgstr "Nhân bản bài tập"
@ -3138,39 +3150,39 @@ msgstr "Bài nộp của %(user)s cho bài %(problem)s"
msgid "All submissions"
msgstr "Tất cả bài nộp"
#: judge/views/submission.py:495
#: judge/views/submission.py:505
msgid "All my submissions"
msgstr "Tất cả bài nộp của tôi"
#: judge/views/submission.py:496
#: judge/views/submission.py:506
#, python-format
msgid "All submissions by %s"
msgstr "Tất cả bài nộp của %s"
#: judge/views/submission.py:541
#: judge/views/submission.py:551
#, python-format
msgid "All submissions for %s"
msgstr "Tất cả bài nộp cho %s"
#: judge/views/submission.py:569
#: judge/views/submission.py:579
msgid "Must pass a problem"
msgstr "Phải làm được một bài"
#: judge/views/submission.py:627
#: judge/views/submission.py:637
#, python-format
msgid "My submissions for %(problem)s"
msgstr "Bài nộp của tôi cho %(problem)s"
#: judge/views/submission.py:628
#: judge/views/submission.py:638
#, python-format
msgid "%(user)s's submissions for %(problem)s"
msgstr "Các bài nộp của %(user)s cho %(problem)s"
#: judge/views/submission.py:758
#: judge/views/submission.py:768
msgid "Must pass a contest"
msgstr "Phải qua một kỳ thi"
#: judge/views/submission.py:788
#: judge/views/submission.py:798
#, python-brace-format
msgid ""
"<a href=\"{1}\">{0}</a>'s submissions for <a href=\"{3}\">{2}</a> in <a href="
@ -3179,7 +3191,7 @@ msgstr ""
"Các bài nộp của <a href=\"{1}\">{0}</a> cho <a href=\"{3}\">{2}</a> trong <a "
"href=\"{5}\">{4}</a>"
#: judge/views/submission.py:800
#: judge/views/submission.py:810
#, python-brace-format
msgid ""
"<a href=\"{1}\">{0}</a>'s submissions for problem {2} in <a href=\"{4}\">{3}"
@ -3188,7 +3200,7 @@ msgstr ""
"Các bài nộp của <a href=\"{1}\">{0}</a> cho bài {2} trong <a href=\"{4}\">{3}"
"</a>"
#: judge/views/submission.py:851
#: judge/views/submission.py:861
#, fuzzy
#| msgid "You do not have the permission to rejudge submissions."
msgid "You don't have permission to access."
@ -3248,48 +3260,48 @@ msgstr "Hủy kích hoạt Two Factor Authentication"
msgid "Perform Two Factor Authentication"
msgstr "Thực hiện Two Factor Authentication"
#: judge/views/user.py:90
#: judge/views/user.py:97
msgid "No such user"
msgstr "Không người dùng nào như vậy"
#: judge/views/user.py:91
#: judge/views/user.py:98
#, python-format
msgid "No user handle \"%s\"."
msgstr "Không tồn tại tên người dùng \"%s\"."
#: judge/views/user.py:96
#: judge/views/user.py:103
msgid "My account"
msgstr "Tài khoản của tôi"
#: judge/views/user.py:98
#: judge/views/user.py:105
#, python-format
msgid "User %s"
msgstr "Thành viên %s"
#: judge/views/user.py:196
#: judge/views/user.py:203
msgid "M j, Y"
msgstr "j M, Y"
#: judge/views/user.py:231
#: judge/views/user.py:238
msgid "M j, Y, G:i"
msgstr "j M, Y, G:i"
#: judge/views/user.py:409
#: judge/views/user.py:417
msgid "Updated on site"
msgstr "Được cập nhật trên web"
#: judge/views/user.py:464 templates/admin/auth/user/change_form.html:14
#: judge/views/user.py:472 templates/admin/auth/user/change_form.html:14
#: templates/admin/auth/user/change_form.html:17 templates/base.html:280
#: templates/user/user-tabs.html:11
msgid "Edit profile"
msgstr "Chỉnh sửa thông tin"
#: judge/views/user.py:475 templates/user/user-left-sidebar.html:2
#: judge/views/user.py:483 templates/user/user-left-sidebar.html:2
#: templates/user/user-list-tabs.html:4
msgid "Leaderboard"
msgstr "Xếp hạng"
#: judge/views/user.py:574
#: judge/views/user.py:582
msgid "Import Users"
msgstr ""

View file

@ -35,6 +35,10 @@
background: #00f9a1;
}
.frozen {
background: #a5c5d9 !important;
}
.rank {
min-width: 2.5em
}