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"), _("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 # We need this flag because `save_related` deals with the inlines, but does not know if we have already rescored
self._rescored = False self._rescored = False
if form.changed_data and any( 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._rescore(obj.key)
self._rescored = True 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.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format 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 from judge.utils.timedelta import nice_repr
@ -54,6 +54,10 @@ class AtCoderContestFormat(DefaultContestFormat):
points = 0 points = 0
format_data = {} 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: with connection.cursor() as cursor:
cursor.execute( cursor.execute(
""" """
@ -66,9 +70,10 @@ class AtCoderContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER 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) judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id GROUP BY cp.id
""", """,
(participation.id, participation.id), (participation.id, participation.id, to_database_time(frozen_time)),
) )
for score, time, prob in cursor.fetchall(): for score, time, prob in cursor.fetchall():
@ -100,6 +105,7 @@ class AtCoderContestFormat(DefaultContestFormat):
format_data[str(prob)] = {"time": dt, "points": score, "penalty": prev} format_data[str(prob)] = {"time": dt, "points": score, "penalty": prev}
points += score points += score
self.handle_frozen_state(participation, format_data)
participation.cumtime = cumtime + penalty participation.cumtime = cumtime + penalty
participation.score = points participation.score = points
participation.tiebreaker = 0 participation.tiebreaker = 0
@ -114,7 +120,7 @@ class AtCoderContestFormat(DefaultContestFormat):
'<small style="color:red"> ({penalty})</small>', '<small style="color:red"> ({penalty})</small>',
penalty=floatformat(format_data["penalty"]), penalty=floatformat(format_data["penalty"]),
) )
if format_data["penalty"] if format_data.get("penalty")
else "" else ""
) )
return format_html( return format_html(
@ -129,6 +135,7 @@ class AtCoderContestFormat(DefaultContestFormat):
+ self.best_solution_state( + self.best_solution_state(
format_data["points"], contest_problem.points format_data["points"], contest_problem.points
) )
+ (" frozen" if format_data.get("frozen") else "")
), ),
url=reverse( url=reverse(
"contest_user_submissions_ajax", "contest_user_submissions_ajax",

View file

@ -1,4 +1,5 @@
from abc import ABCMeta, abstractmethod, abstractproperty from abc import ABCMeta, abstractmethod, abstractproperty
from django.db.models import Max
class abstractclassmethod(classmethod): class abstractclassmethod(classmethod):
@ -95,3 +96,17 @@ class BaseContestFormat(metaclass=ABCMeta):
if points == total: if points == total:
return "full-score" return "full-score"
return "partial-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 points = 0
format_data = {} 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"), time=Max("submission__date"),
points=Max("points"), points=Max("points"),
): )
for result in queryset:
dt = (result["time"] - participation.start).total_seconds() dt = (result["time"] - participation.start).total_seconds()
if result["points"]: if result["points"]:
cumtime += dt cumtime += dt
@ -45,6 +54,7 @@ class DefaultContestFormat(BaseContestFormat):
} }
points += result["points"] points += result["points"]
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(cumtime, 0) participation.cumtime = max(cumtime, 0)
participation.score = points participation.score = points
participation.tiebreaker = 0 participation.tiebreaker = 0
@ -66,6 +76,7 @@ class DefaultContestFormat(BaseContestFormat):
+ self.best_solution_state( + self.best_solution_state(
format_data["points"], contest_problem.points format_data["points"], contest_problem.points
) )
+ (" frozen" if format_data.get("frozen") else "")
), ),
url=reverse( url=reverse(
"contest_user_submissions_ajax", "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.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format 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 from judge.utils.timedelta import nice_repr
@ -60,6 +60,10 @@ class ECOOContestFormat(DefaultContestFormat):
points = 0 points = 0
format_data = {} 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: with connection.cursor() as cursor:
cursor.execute( cursor.execute(
""" """
@ -77,9 +81,15 @@ class ECOOContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER 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) judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id 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(): 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} format_data[str(prob)] = {"time": dt, "points": score, "bonus": bonus}
points += score points += score
self.handle_frozen_state(participation, format_data)
participation.cumtime = cumtime participation.cumtime = cumtime
participation.score = points participation.score = points
participation.tiebreaker = 0 participation.tiebreaker = 0
@ -134,6 +145,7 @@ class ECOOContestFormat(DefaultContestFormat):
+ self.best_solution_state( + self.best_solution_state(
format_data["points"], contest_problem.points format_data["points"], contest_problem.points
) )
+ (" frozen" if format_data.get("frozen") else "")
), ),
url=reverse( url=reverse(
"contest_user_submissions_ajax", "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.default import DefaultContestFormat
from judge.contest_format.registry import register_contest_format 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 from judge.utils.timedelta import nice_repr
@ -55,6 +55,10 @@ class ICPCContestFormat(DefaultContestFormat):
score = 0 score = 0
format_data = {} 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: with connection.cursor() as cursor:
cursor.execute( cursor.execute(
""" """
@ -67,9 +71,10 @@ class ICPCContestFormat(DefaultContestFormat):
FROM judge_contestproblem cp INNER JOIN FROM judge_contestproblem cp INNER JOIN
judge_contestsubmission cs ON (cs.problem_id = cp.id AND cs.participation_id = %s) LEFT OUTER 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) judge_submission sub ON (sub.id = cs.submission_id)
WHERE sub.date < %s
GROUP BY cp.id GROUP BY cp.id
""", """,
(participation.id, participation.id), (participation.id, participation.id, to_database_time(frozen_time)),
) )
for points, time, prob in cursor.fetchall(): for points, time, prob in cursor.fetchall():
@ -102,6 +107,7 @@ class ICPCContestFormat(DefaultContestFormat):
format_data[str(prob)] = {"time": dt, "points": points, "penalty": prev} format_data[str(prob)] = {"time": dt, "points": points, "penalty": prev}
score += points score += points
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(0, cumtime + penalty) participation.cumtime = max(0, cumtime + penalty)
participation.score = score participation.score = score
participation.tiebreaker = last # field is sorted from least to greatest participation.tiebreaker = last # field is sorted from least to greatest
@ -116,7 +122,7 @@ class ICPCContestFormat(DefaultContestFormat):
'<small style="color:red"> +{penalty}</small>', '<small style="color:red"> +{penalty}</small>',
penalty=floatformat(format_data["penalty"]), penalty=floatformat(format_data["penalty"]),
) )
if format_data["penalty"] if format_data.get("penalty")
else "" else ""
) )
return format_html( return format_html(
@ -131,6 +137,7 @@ class ICPCContestFormat(DefaultContestFormat):
+ self.best_solution_state( + self.best_solution_state(
format_data["points"], contest_problem.points format_data["points"], contest_problem.points
) )
+ (" frozen" if format_data.get("frozen") else "")
), ),
url=reverse( url=reverse(
"contest_user_submissions_ajax", "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.contest_format.registry import register_contest_format
from judge.timezone import from_database_time from judge.timezone import from_database_time
from judge.utils.timedelta import nice_repr from judge.utils.timedelta import nice_repr
from django.db.models import Min, OuterRef, Subquery
@register_contest_format("ioi") @register_contest_format("ioi")
@ -45,41 +46,42 @@ class IOIContestFormat(DefaultContestFormat):
def update_participation(self, participation): def update_participation(self, participation):
cumtime = 0 cumtime = 0
points = 0 score = 0
format_data = {} format_data = {}
with connection.cursor() as cursor: queryset = participation.submissions
cursor.execute( if self.contest.freeze_after:
""" queryset = queryset.filter(
SELECT MAX(cs.points) as `score`, ( submission__date__lt=participation.start + self.contest.freeze_after
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),
) )
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"]: if self.config["cumtime"]:
dt = ( dt = (time - participation.start).total_seconds()
from_database_time(time) - participation.start if points:
).total_seconds()
if score:
cumtime += dt cumtime += dt
else: else:
dt = 0 dt = 0
format_data[str(prob)] = {"time": dt, "points": score} format_data[str(problem_id)] = {"points": points, "time": dt}
points += score score += points
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(cumtime, 0) participation.cumtime = max(cumtime, 0)
participation.score = points participation.score = score
participation.tiebreaker = 0 participation.tiebreaker = 0
participation.format_data = format_data participation.format_data = format_data
participation.save() participation.save()
@ -99,6 +101,7 @@ class IOIContestFormat(DefaultContestFormat):
+ self.best_solution_state( + self.best_solution_state(
format_data["points"], contest_problem.points format_data["points"], contest_problem.points
) )
+ (" frozen" if format_data.get("frozen") else "")
), ),
url=reverse( url=reverse(
"contest_user_submissions_ajax", "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" "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( is_visible = models.BooleanField(
verbose_name=_("publicly visible"), verbose_name=_("publicly visible"),
default=False, default=False,

View file

@ -2,7 +2,7 @@ import pytz
from django.conf import settings from django.conf import settings
from django.db import connection from django.db import connection
from django.utils import timezone from django.utils import timezone
from django.utils.timezone import make_aware from django.utils.timezone import make_aware, make_naive
class TimezoneMiddleware(object): class TimezoneMiddleware(object):
@ -25,3 +25,10 @@ def from_database_time(datetime):
if tz is None: if tz is None:
return datetime return datetime
return make_aware(datetime, tz) 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" template_name = "submission/list.html"
context_object_name = "submissions" context_object_name = "submissions"
first_page_href = None first_page_href = None
include_frozen = False
def get_result_data(self): def get_result_data(self):
result = self._get_result_data() result = self._get_result_data()
@ -344,6 +345,10 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
queryset = queryset.filter(contest_object=self.contest) queryset = queryset.filter(contest_object=self.contest)
if not self.contest.can_see_full_scoreboard(self.request.user): if not self.contest.can_see_full_scoreboard(self.request.user):
queryset = queryset.filter(user=self.request.profile) 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: else:
queryset = queryset.select_related("contest_object").defer( queryset = queryset.select_related("contest_object").defer(
"contest_object__description" "contest_object__description"
@ -456,6 +461,9 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
self.selected_languages = set(request.GET.getlist("language")) self.selected_languages = set(request.GET.getlist("language"))
self.selected_statuses = set(request.GET.getlist("status")) self.selected_statuses = set(request.GET.getlist("status"))
if request.user.is_superuser:
self.include_frozen = True
if "results" in request.GET: if "results" in request.GET:
return JsonResponse(self.get_result_data()) return JsonResponse(self.get_result_data())
@ -468,6 +476,8 @@ class UserMixin(object):
raise ImproperlyConfigured("Must pass a user") raise ImproperlyConfigured("Must pass a user")
self.profile = get_object_or_404(Profile, user__username=kwargs["user"]) self.profile = get_object_or_404(Profile, user__username=kwargs["user"])
self.username = kwargs["user"] self.username = kwargs["user"]
if self.profile == request.profile:
self.include_frozen = True
return super(UserMixin, self).get(request, *args, **kwargs) return super(UserMixin, self).get(request, *args, **kwargs)
@ -837,8 +847,8 @@ class UserContestSubmissionsAjax(UserContestSubmissions):
s.contest_time = nice_repr(contest_time, "noday") s.contest_time = nice_repr(contest_time, "noday")
else: else:
s.contest_time = None s.contest_time = None
points = floatformat(s.contest.points, -self.contest.points_precision)
total = floatformat(contest_problem.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}" s.display_point = f"{points} / {total}"
filtered_submissions.append(s) filtered_submissions.append(s)
context["submissions"] = filtered_submissions context["submissions"] = filtered_submissions

View file

@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: lqdoj2\n" "Project-Id-Version: lqdoj2\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: 2021-07-20 03:44\n"
"Last-Translator: Icyene\n" "Last-Translator: Icyene\n"
"Language-Team: Vietnamese\n" "Language-Team: Vietnamese\n"
@ -19,8 +19,8 @@ msgstr ""
"X-Crowdin-File-ID: 5\n" "X-Crowdin-File-ID: 5\n"
#: chat_box/models.py:31 chat_box/models.py:51 chat_box/models.py:65 #: 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/admin/interface.py:150 judge/models/contest.py:630
#: judge/models/contest.py:815 judge/models/profile.py:354 #: judge/models/contest.py:823 judge/models/profile.py:354
#: judge/models/profile.py:430 #: judge/models/profile.py:430
msgid "user" msgid "user"
msgstr "người dùng" msgstr "người dùng"
@ -99,72 +99,72 @@ msgstr "Bài tập"
msgid "Settings" msgid "Settings"
msgstr "Cài đặt" msgstr "Cài đặt"
#: judge/admin/contest.py:161 #: judge/admin/contest.py:162
msgid "Scheduling" msgid "Scheduling"
msgstr "" msgstr ""
#: judge/admin/contest.py:163 #: judge/admin/contest.py:166
msgid "Details" msgid "Details"
msgstr "Chi tiết" msgstr "Chi tiết"
#: judge/admin/contest.py:175 #: judge/admin/contest.py:178
msgid "Format" msgid "Format"
msgstr "Thể thức" 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 #: templates/user/user-about.html:15 templates/user/user-about.html:45
msgid "Rating" msgid "Rating"
msgstr "" msgstr ""
#: judge/admin/contest.py:191 #: judge/admin/contest.py:194
msgid "Access" msgid "Access"
msgstr "Truy cập" 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" msgid "Justice"
msgstr "Xử phạt" msgstr "Xử phạt"
#: judge/admin/contest.py:313 #: judge/admin/contest.py:317
#, python-format #, python-format
msgid "%d contest successfully marked as visible." msgid "%d contest successfully marked as visible."
msgid_plural "%d contests successfully marked as visible." msgid_plural "%d contests successfully marked as visible."
msgstr[0] "%d kỳ thi đã được đánh dấu hiển thị." 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" msgid "Mark contests as visible"
msgstr "Đánh dấu hiển thị các kỳ thi" msgstr "Đánh dấu hiển thị các kỳ thi"
#: judge/admin/contest.py:331 #: judge/admin/contest.py:335
#, python-format #, python-format
msgid "%d contest successfully marked as hidden." msgid "%d contest successfully marked as hidden."
msgid_plural "%d contests successfully marked as hidden." msgid_plural "%d contests successfully marked as hidden."
msgstr[0] "%d kỳ thi đã được đánh dấu ẩn." 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" msgid "Mark contests as hidden"
msgstr "Ẩn các kỳ thi" 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 #, python-format
msgid "%d submission was successfully scheduled for rejudging." msgid "%d submission was successfully scheduled for rejudging."
msgid_plural "%d submissions were 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." 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 #, python-format
msgid "%d participation recalculated." msgid "%d participation recalculated."
msgid_plural "%d participations recalculated." msgid_plural "%d participations recalculated."
msgstr[0] "%d thí sinh đã được tính điểm lại." 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" msgid "Recalculate results"
msgstr "Tính toán lại kết quả" 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" msgid "username"
msgstr "tên đăng nhập" 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" msgid "virtual"
msgstr "ảo" msgstr "ảo"
@ -567,13 +567,13 @@ msgstr "Lưu"
msgid "bookmarks" msgid "bookmarks"
msgstr "Lưu" msgstr "Lưu"
#: judge/models/bookmark.py:56 #: judge/models/bookmark.py:59
#, fuzzy #, fuzzy
#| msgid "Bookmark" #| msgid "Bookmark"
msgid "make bookmark" msgid "make bookmark"
msgstr "Lưu" msgstr "Lưu"
#: judge/models/bookmark.py:57 #: judge/models/bookmark.py:60
#, fuzzy #, fuzzy
#| msgid "Bookmark" #| msgid "Bookmark"
msgid "make bookmarks" msgid "make bookmarks"
@ -696,7 +696,7 @@ msgstr ""
msgid "contest tag" msgid "contest tag"
msgstr "" msgstr ""
#: judge/models/contest.py:74 judge/models/contest.py:234 #: judge/models/contest.py:74 judge/models/contest.py:242
msgid "contest tags" msgid "contest tags"
msgstr "nhãn kỳ thi" msgstr "nhãn kỳ thi"
@ -751,7 +751,7 @@ msgstr "mô tả"
msgid "problems" msgid "problems"
msgstr "bài tập" 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" msgid "start time"
msgstr "thời gian bắt đầu" 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 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" "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" msgid "publicly visible"
msgstr "công khai" msgstr "công khai"
#: judge/models/contest.py:135 #: judge/models/contest.py:143
msgid "" msgid ""
"Should be set even for organization-private contests, where it determines " "Should be set even for organization-private contests, where it determines "
"whether the contest is visible to members of the specified organizations." "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ó " "Đá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." "đượ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" msgid "contest rated"
msgstr "kỳ thi được xếp hạng" 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." msgid "Whether this contest can be rated."
msgstr "Quyết định kỳ thi có được xếp hạng không." 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" msgid "scoreboard visibility"
msgstr "khả năng hiển thị của bảng điểm" 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" 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" 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" msgid "view contest scoreboard"
msgstr "xem bảng điểm kỳ thi" 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." 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." 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" msgid "no comments"
msgstr "không bình luận" msgstr "không bình luận"
#: judge/models/contest.py:161 #: judge/models/contest.py:169
msgid "Use clarification system instead of comments." msgid "Use clarification system instead of comments."
msgstr "Dùng hệ thống thông báo thay vì bình luận." 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" msgid "Rating floor for contest"
msgstr "Cận dưới rating được xếp hạng trong kỳ thi" 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" msgid "Rating ceiling for contest"
msgstr "Cận trên rating được xếp hạng trong kỳ thi" 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" msgid "rate all"
msgstr "xếp hạng tất cả" msgstr "xếp hạng tất cả"
#: judge/models/contest.py:178 #: judge/models/contest.py:186
msgid "Rate all users who joined." 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)." 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" msgid "exclude from ratings"
msgstr "không xếp hạng" msgstr "không xếp hạng"
#: judge/models/contest.py:188 #: judge/models/contest.py:196
msgid "private to specific users" msgid "private to specific users"
msgstr "riêng tư với các người dùng này" 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" msgid "private contestants"
msgstr "thí sinh riêng tư" 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" 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" 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" msgid "hide problem tags"
msgstr "ẩn nhãn kỳ thi" 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." msgid "Whether problem tags should be hidden by default."
msgstr "" msgstr ""
"Quyết định việc nhãn bài tập (DP, Tham lam, ...) được ẩn trong kỳ thi không." "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" msgid "run pretests only"
msgstr "chỉ chạy pretests" msgstr "chỉ chạy pretests"
#: judge/models/contest.py:205 #: judge/models/contest.py:213
msgid "" msgid ""
"Whether judges should grade pretests only, versus all testcases. Commonly " "Whether judges should grade pretests only, versus all testcases. Commonly "
"set during a contest, then unset prior to rejudging user submissions when " "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 " "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." "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 #: judge/models/problem.py:279
msgid "private to organizations" msgid "private to organizations"
msgstr "riêng tư với các tổ chức" 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 #: judge/models/problem.py:275 judge/models/profile.py:126
msgid "organizations" msgid "organizations"
msgstr "tổ chức" msgstr "tổ chức"
#: judge/models/contest.py:218 #: judge/models/contest.py:226
msgid "If private, only these organizations may see the contest" 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" 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" msgid "OpenGraph image"
msgstr "Hình ảnh OpenGraph" 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" msgid "Logo override image"
msgstr "Hình ảnh ghi đè logo" msgstr "Hình ảnh ghi đè logo"
#: judge/models/contest.py:229 #: judge/models/contest.py:237
msgid "" msgid ""
"This image will replace the default site logo for users inside the contest." "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." 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" msgid "the amount of live participants"
msgstr "số lượng thí sinh thi trực tiếp" msgstr "số lượng thí sinh thi trực tiếp"
#: judge/models/contest.py:241 #: judge/models/contest.py:249
msgid "contest summary" msgid "contest summary"
msgstr "tổng kết kỳ thi" 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." msgid "Plain-text, shown in meta description tag, e.g. for social media."
msgstr "" msgstr ""
#: judge/models/contest.py:247 judge/models/profile.py:76 #: judge/models/contest.py:255 judge/models/profile.py:76
msgid "access code" msgid "access code"
msgstr "mật khẩu truy cập" msgstr "mật khẩu truy cập"
#: judge/models/contest.py:252 #: judge/models/contest.py:260
msgid "" msgid ""
"An optional code to prompt contestants before they are allowed to join the " "An optional code to prompt contestants before they are allowed to join the "
"contest. Leave it blank to disable." "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 " "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." "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" msgid "personae non gratae"
msgstr "Chặn tham gia" msgstr "Chặn tham gia"
#: judge/models/contest.py:260 #: judge/models/contest.py:268
msgid "Bans the selected users from joining this contest." 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." 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" msgid "contest format"
msgstr "format kỳ thi" msgstr "format kỳ thi"
#: judge/models/contest.py:267 #: judge/models/contest.py:275
msgid "The contest format module to use." msgid "The contest format module to use."
msgstr "Format kỳ thi sử dụng." msgstr "Format kỳ thi sử dụng."
#: judge/models/contest.py:270 #: judge/models/contest.py:278
msgid "contest format configuration" msgid "contest format configuration"
msgstr "Tùy chỉnh format kỳ thi" msgstr "Tùy chỉnh format kỳ thi"
#: judge/models/contest.py:274 #: judge/models/contest.py:282
msgid "" msgid ""
"A JSON object to serve as the configuration for the chosen contest format " "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 " "module. Leave empty to use None. Exact format depends on the contest format "
"selected." "selected."
msgstr "" msgstr ""
#: judge/models/contest.py:287 #: judge/models/contest.py:295
msgid "precision points" msgid "precision points"
msgstr "Hiển thị điểm" msgstr "Hiển thị điểm"
#: judge/models/contest.py:290 #: judge/models/contest.py:298
msgid "Number of digits to round points to." msgid "Number of digits to round points to."
msgstr "Số chữ số thập phân trên bảng điểm." 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" msgid "See private contests"
msgstr "" msgstr ""
#: judge/models/contest.py:596 #: judge/models/contest.py:604
msgid "Edit own contests" msgid "Edit own contests"
msgstr "" msgstr ""
#: judge/models/contest.py:597 #: judge/models/contest.py:605
msgid "Edit all contests" msgid "Edit all contests"
msgstr "" msgstr ""
#: judge/models/contest.py:598 #: judge/models/contest.py:606
msgid "Clone contest" msgid "Clone contest"
msgstr "" msgstr ""
#: judge/models/contest.py:599 templates/contest/moss.html:74 #: judge/models/contest.py:607 templates/contest/moss.html:74
msgid "MOSS contest" msgid "MOSS contest"
msgstr "" msgstr ""
#: judge/models/contest.py:600 #: judge/models/contest.py:608
msgid "Rate contests" msgid "Rate contests"
msgstr "" msgstr ""
#: judge/models/contest.py:601 #: judge/models/contest.py:609
msgid "Contest access codes" msgid "Contest access codes"
msgstr "" msgstr ""
#: judge/models/contest.py:602 #: judge/models/contest.py:610
msgid "Create private contests" msgid "Create private contests"
msgstr "" msgstr ""
#: judge/models/contest.py:603 #: judge/models/contest.py:611
msgid "Change contest visibility" msgid "Change contest visibility"
msgstr "" msgstr ""
#: judge/models/contest.py:604 #: judge/models/contest.py:612
msgid "Edit contest problem label script" msgid "Edit contest problem label script"
msgstr "Cách hiển thị thứ tự bài tập" 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:614 judge/models/contest.py:752
#: judge/models/contest.py:818 judge/models/contest.py:848 #: judge/models/contest.py:826 judge/models/contest.py:856
#: judge/models/submission.py:116 #: judge/models/submission.py:116
msgid "contest" msgid "contest"
msgstr "kỳ thi" msgstr "kỳ thi"
#: judge/models/contest.py:607 #: judge/models/contest.py:615
msgid "contests" msgid "contests"
msgstr "kỳ thi" msgstr "kỳ thi"
#: judge/models/contest.py:616 #: judge/models/contest.py:624
msgid "associated contest" msgid "associated contest"
msgstr "" msgstr ""
#: judge/models/contest.py:629 #: judge/models/contest.py:637
msgid "score" msgid "score"
msgstr "điểm" msgstr "điểm"
#: judge/models/contest.py:630 #: judge/models/contest.py:638
msgid "cumulative time" msgid "cumulative time"
msgstr "tổng thời gian" msgstr "tổng thời gian"
#: judge/models/contest.py:632 #: judge/models/contest.py:640
msgid "is disqualified" msgid "is disqualified"
msgstr "đã bị loại" msgstr "đã bị loại"
#: judge/models/contest.py:634 #: judge/models/contest.py:642
msgid "Whether this participation is disqualified." msgid "Whether this participation is disqualified."
msgstr "Quyết định thí sinh có bị loại không." 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" msgid "tie-breaking field"
msgstr "" msgstr ""
#: judge/models/contest.py:638 #: judge/models/contest.py:646
msgid "virtual participation id" msgid "virtual participation id"
msgstr "id lần tham gia ảo" 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." 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." 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" msgid "contest format specific data"
msgstr "" msgstr ""
#: judge/models/contest.py:719 #: judge/models/contest.py:727
#, python-format #, python-format
msgid "%s spectating in %s" msgid "%s spectating in %s"
msgstr "%s đang theo dõi trong %s" msgstr "%s đang theo dõi trong %s"
#: judge/models/contest.py:724 #: judge/models/contest.py:732
#, python-format #, python-format
msgid "%s in %s, v%d" msgid "%s in %s, v%d"
msgstr "%s trong %s, v%d" msgstr "%s trong %s, v%d"
#: judge/models/contest.py:729 #: judge/models/contest.py:737
#, python-format #, python-format
msgid "%s in %s" msgid "%s in %s"
msgstr "%s trong %s" msgstr "%s trong %s"
#: judge/models/contest.py:732 #: judge/models/contest.py:740
msgid "contest participation" msgid "contest participation"
msgstr "lần tham gia kỳ thi" msgstr "lần tham gia kỳ thi"
#: judge/models/contest.py:733 #: judge/models/contest.py:741
msgid "contest participations" msgid "contest participations"
msgstr "lần tham gia kỳ thi" msgstr "lần tham gia kỳ thi"
#: judge/models/contest.py:740 judge/models/contest.py:789 #: judge/models/contest.py:748 judge/models/contest.py:797
#: judge/models/contest.py:851 judge/models/problem.py:558 #: judge/models/contest.py:859 judge/models/problem.py:558
#: judge/models/problem.py:565 judge/models/problem.py:586 #: judge/models/problem.py:565 judge/models/problem.py:586
#: judge/models/problem.py:617 judge/models/problem_data.py:50 #: judge/models/problem.py:617 judge/models/problem_data.py:50
msgid "problem" msgid "problem"
msgstr "bài tập" 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 #: judge/models/problem.py:206
msgid "points" msgid "points"
msgstr "điểm" msgstr "điểm"
#: judge/models/contest.py:749 #: judge/models/contest.py:757
msgid "partial" msgid "partial"
msgstr "thành phần" 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" msgid "is pretested"
msgstr "dùng pretest" 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" msgid "order"
msgstr "thứ tự" msgstr "thứ tự"
#: judge/models/contest.py:753 #: judge/models/contest.py:761
msgid "0 to not show testcases, 1 to show" msgid "0 to not show testcases, 1 to show"
msgstr "0 để ẩn test, 1 để hiện" msgstr "0 để ẩn test, 1 để hiện"
#: judge/models/contest.py:754 #: judge/models/contest.py:762
msgid "visible testcases" msgid "visible testcases"
msgstr "hiển thị test" 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." 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." 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" msgid "max submissions"
msgstr "số lần nộp tối đa" 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?" msgid "Why include a problem you can't submit to?"
msgstr "" msgstr ""
#: judge/models/contest.py:776 #: judge/models/contest.py:784
msgid "contest problem" msgid "contest problem"
msgstr "bài trong kỳ thi" msgstr "bài trong kỳ thi"
#: judge/models/contest.py:777 #: judge/models/contest.py:785
msgid "contest problems" msgid "contest problems"
msgstr "bài trong kỳ thi" 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" msgid "submission"
msgstr "bài nộp" 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" msgid "participation"
msgstr "lần tham gia" msgstr "lần tham gia"
#: judge/models/contest.py:804 #: judge/models/contest.py:812
msgid "Whether this submission was ran only on pretests." 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." 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" msgid "contest submission"
msgstr "bài nộp kỳ thi" msgstr "bài nộp kỳ thi"
#: judge/models/contest.py:810 #: judge/models/contest.py:818
msgid "contest submissions" msgid "contest submissions"
msgstr "bài nộp kỳ thi" msgstr "bài nộp kỳ thi"
#: judge/models/contest.py:826 #: judge/models/contest.py:834
msgid "rank" msgid "rank"
msgstr "rank" msgstr "rank"
#: judge/models/contest.py:827 #: judge/models/contest.py:835
msgid "rating" msgid "rating"
msgstr "rating" msgstr "rating"
#: judge/models/contest.py:828 #: judge/models/contest.py:836
msgid "raw rating" msgid "raw rating"
msgstr "rating thật" msgstr "rating thật"
#: judge/models/contest.py:829 #: judge/models/contest.py:837
msgid "contest performance" msgid "contest performance"
msgstr "" msgstr ""
#: judge/models/contest.py:830 #: judge/models/contest.py:838
msgid "last rated" msgid "last rated"
msgstr "lần cuối được xếp hạng" msgstr "lần cuối được xếp hạng"
#: judge/models/contest.py:834 #: judge/models/contest.py:842
msgid "contest rating" msgid "contest rating"
msgstr "rating kỳ thi" msgstr "rating kỳ thi"
#: judge/models/contest.py:835 #: judge/models/contest.py:843
msgid "contest ratings" msgid "contest ratings"
msgstr "rating kỳ thi" msgstr "rating kỳ thi"
#: judge/models/contest.py:859 #: judge/models/contest.py:867
msgid "contest moss result" msgid "contest moss result"
msgstr "kết quả MOSS kỳ thi" msgstr "kết quả MOSS kỳ thi"
#: judge/models/contest.py:860 #: judge/models/contest.py:868
msgid "contest moss results" msgid "contest moss results"
msgstr "kết quả MOSS kỳ thi" msgstr "kết quả MOSS kỳ thi"
#: judge/models/contest.py:865 #: judge/models/contest.py:873
msgid "clarified problem" msgid "clarified problem"
msgstr "" msgstr ""
#: judge/models/contest.py:867 #: judge/models/contest.py:875
msgid "clarification body" msgid "clarification body"
msgstr "" msgstr ""
#: judge/models/contest.py:869 #: judge/models/contest.py:877
msgid "clarification timestamp" msgid "clarification timestamp"
msgstr "" msgstr ""
@ -2554,7 +2566,7 @@ msgid "Editing comment"
msgstr "Chỉnh sửa bình luận" msgstr "Chỉnh sửa bình luận"
#: judge/views/contests.py:119 judge/views/contests.py:367 #: 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" msgid "No such contest"
msgstr "Không có contest nào như vậy" 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" msgid "Access to contest \"%s\" denied"
msgstr "Truy cập tới kỳ thi \"%s\" bị từ chối" 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" msgid "Clone Contest"
msgstr "Nhân bản kỳ thi" msgstr "Nhân bản kỳ thi"
#: judge/views/contests.py:487 #: judge/views/contests.py:493
msgid "Contest not ongoing" msgid "Contest not ongoing"
msgstr "Kỳ thi đang không diễn ra" msgstr "Kỳ thi đang không diễn ra"
#: judge/views/contests.py:488 #: judge/views/contests.py:494
#, python-format #, python-format
msgid "\"%s\" is not currently ongoing." msgid "\"%s\" is not currently ongoing."
msgstr "\"%s\" kỳ thi đang không diễn ra." msgstr "\"%s\" kỳ thi đang không diễn ra."
#: judge/views/contests.py:495 #: judge/views/contests.py:501
msgid "Already in contest" msgid "Already in contest"
msgstr "Đã ở trong kỳ thi" msgstr "Đã ở trong kỳ thi"
#: judge/views/contests.py:496 #: judge/views/contests.py:502
#, python-format #, python-format
msgid "You are already in a contest: \"%s\"." msgid "You are already in a contest: \"%s\"."
msgstr "Bạn đã ở trong kỳ thi: \"%s\"." msgstr "Bạn đã ở trong kỳ thi: \"%s\"."
#: judge/views/contests.py:506 #: judge/views/contests.py:512
msgid "Banned from joining" msgid "Banned from joining"
msgstr "Bị cấm tham gia" msgstr "Bị cấm tham gia"
#: judge/views/contests.py:508 #: judge/views/contests.py:514
msgid "" msgid ""
"You have been declared persona non grata for this contest. You are " "You have been declared persona non grata for this contest. You are "
"permanently barred from joining this contest." "permanently barred from joining this contest."
msgstr "Bạn không được phép tham gia kỳ thi này." 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 #, python-format
msgid "Enter access code for \"%s\"" msgid "Enter access code for \"%s\""
msgstr "Nhập mật khẩu truy cập cho \"%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 #, python-format
msgid "You are not in contest \"%s\"." msgid "You are not in contest \"%s\"."
msgstr "Bạn không ở trong kỳ thi \"%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" 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" 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 #, python-format
msgid "Contests in %(month)s" msgid "Contests in %(month)s"
msgstr "Các kỳ thi trong %(month)s" msgstr "Các kỳ thi trong %(month)s"
#: judge/views/contests.py:696 #: judge/views/contests.py:702
msgid "F Y" msgid "F Y"
msgstr "F Y" msgstr "F Y"
#: judge/views/contests.py:756 #: judge/views/contests.py:762
#, python-format #, python-format
msgid "%s Statistics" msgid "%s Statistics"
msgstr "%s Thống kê" msgstr "%s Thống kê"
#: judge/views/contests.py:1013 #: judge/views/contests.py:1019
#, python-format #, python-format
msgid "%s Rankings" msgid "%s Rankings"
msgstr "%s Bảng điểm" msgstr "%s Bảng điểm"
#: judge/views/contests.py:1024 #: judge/views/contests.py:1030
msgid "???" msgid "???"
msgstr "???" msgstr "???"
#: judge/views/contests.py:1040 #: judge/views/contests.py:1046
#, python-format #, python-format
msgid "Your participation in %s" msgid "Your participation in %s"
msgstr "Lần tham gia trong %s" msgstr "Lần tham gia trong %s"
#: judge/views/contests.py:1041 #: judge/views/contests.py:1047
#, python-format #, python-format
msgid "%s's participation in %s" msgid "%s's participation in %s"
msgstr "Lần tham gia của %s trong %s" msgstr "Lần tham gia của %s trong %s"
#: judge/views/contests.py:1055 #: judge/views/contests.py:1061
msgid "Live" msgid "Live"
msgstr "Trực tiếp" 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" msgid "Participation"
msgstr "Lần tham gia" msgstr "Lần tham gia"
#: judge/views/contests.py:1123 #: judge/views/contests.py:1129
#, python-format #, python-format
msgid "%s MOSS Results" msgid "%s MOSS Results"
msgstr "%s Kết quả MOSS" msgstr "%s Kết quả MOSS"
#: judge/views/contests.py:1159 #: judge/views/contests.py:1165
#, python-format #, python-format
msgid "Running MOSS for %s..." msgid "Running MOSS for %s..."
msgstr "Đang chạy MOSS cho %s..." msgstr "Đang chạy MOSS cho %s..."
#: judge/views/contests.py:1182 #: judge/views/contests.py:1188
#, python-format #, python-format
msgid "Contest tag: %s" msgid "Contest tag: %s"
msgstr "Nhãn kỳ thi: %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" msgid "Issue description"
msgstr "Mô tả vấn đề" msgstr "Mô tả vấn đề"
#: judge/views/contests.py:1244 #: judge/views/contests.py:1250
#, python-format #, python-format
msgid "New clarification for %s" msgid "New clarification for %s"
msgstr "Thông báo mới cho %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>" msgid "Editorial for <a href=\"{1}\">{0}</a>"
msgstr "Hướng dẫn cho <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/organization/org-left-sidebar.html:4
#: templates/user/user-about.html:28 templates/user/user-bookmarks.html:32 #: templates/user/user-about.html:28 templates/user/user-bookmarks.html:32
#: templates/user/user-tabs.html:5 templates/user/users-table.html:19 #: templates/user/user-tabs.html:5 templates/user/users-table.html:19
msgid "Problems" msgid "Problems"
msgstr "Bài tập" msgstr "Bài tập"
#: judge/views/problem.py:823 #: judge/views/problem.py:827
msgid "Problem feed" msgid "Problem feed"
msgstr "Bài tập" msgstr "Bài tập"
#: judge/views/problem.py:1061 #: judge/views/problem.py:1065
msgid "Banned from submitting" msgid "Banned from submitting"
msgstr "Bị cấm nộp bài" msgstr "Bị cấm nộp bài"
#: judge/views/problem.py:1063 #: judge/views/problem.py:1067
msgid "" msgid ""
"You have been declared persona non grata for this problem. You are " "You have been declared persona non grata for this problem. You are "
"permanently barred from submitting this problem." "permanently barred from submitting this problem."
msgstr "Bạn đã bị cấm nộp bài này." 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" msgid "Too many submissions"
msgstr "Quá nhiều lần nộp" 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." 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." 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 #, python-format
msgid "Submit to %(problem)s" msgid "Submit to %(problem)s"
msgstr "Nộp bài cho %(problem)s" msgstr "Nộp bài cho %(problem)s"
#: judge/views/problem.py:1195 #: judge/views/problem.py:1199
msgid "Clone Problem" msgid "Clone Problem"
msgstr "Nhân bản bài tập" 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" msgid "All submissions"
msgstr "Tất cả bài nộp" msgstr "Tất cả bài nộp"
#: judge/views/submission.py:495 #: judge/views/submission.py:505
msgid "All my submissions" msgid "All my submissions"
msgstr "Tất cả bài nộp của tôi" msgstr "Tất cả bài nộp của tôi"
#: judge/views/submission.py:496 #: judge/views/submission.py:506
#, python-format #, python-format
msgid "All submissions by %s" msgid "All submissions by %s"
msgstr "Tất cả bài nộp của %s" msgstr "Tất cả bài nộp của %s"
#: judge/views/submission.py:541 #: judge/views/submission.py:551
#, python-format #, python-format
msgid "All submissions for %s" msgid "All submissions for %s"
msgstr "Tất cả bài nộp cho %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" msgid "Must pass a problem"
msgstr "Phải làm được một bài" msgstr "Phải làm được một bài"
#: judge/views/submission.py:627 #: judge/views/submission.py:637
#, python-format #, python-format
msgid "My submissions for %(problem)s" msgid "My submissions for %(problem)s"
msgstr "Bài nộp của tôi cho %(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 #, python-format
msgid "%(user)s's submissions for %(problem)s" msgid "%(user)s's submissions for %(problem)s"
msgstr "Các bài nộp của %(user)s cho %(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" msgid "Must pass a contest"
msgstr "Phải qua một kỳ thi" msgstr "Phải qua một kỳ thi"
#: judge/views/submission.py:788 #: judge/views/submission.py:798
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<a href=\"{1}\">{0}</a>'s submissions for <a href=\"{3}\">{2}</a> in <a href=" "<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 " "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>" "href=\"{5}\">{4}</a>"
#: judge/views/submission.py:800 #: judge/views/submission.py:810
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"<a href=\"{1}\">{0}</a>'s submissions for problem {2} in <a href=\"{4}\">{3}" "<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}" "Các bài nộp của <a href=\"{1}\">{0}</a> cho bài {2} trong <a href=\"{4}\">{3}"
"</a>" "</a>"
#: judge/views/submission.py:851 #: judge/views/submission.py:861
#, fuzzy #, fuzzy
#| msgid "You do not have the permission to rejudge submissions." #| msgid "You do not have the permission to rejudge submissions."
msgid "You don't have permission to access." 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" msgid "Perform Two Factor Authentication"
msgstr "Thực hiện 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" msgid "No such user"
msgstr "Không người dùng nào như vậy" msgstr "Không người dùng nào như vậy"
#: judge/views/user.py:91 #: judge/views/user.py:98
#, python-format #, python-format
msgid "No user handle \"%s\"." msgid "No user handle \"%s\"."
msgstr "Không tồn tại tên người dùng \"%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" msgid "My account"
msgstr "Tài khoản của tôi" msgstr "Tài khoản của tôi"
#: judge/views/user.py:98 #: judge/views/user.py:105
#, python-format #, python-format
msgid "User %s" msgid "User %s"
msgstr "Thành viên %s" msgstr "Thành viên %s"
#: judge/views/user.py:196 #: judge/views/user.py:203
msgid "M j, Y" msgid "M j, Y"
msgstr "j M, Y" msgstr "j M, Y"
#: judge/views/user.py:231 #: judge/views/user.py:238
msgid "M j, Y, G:i" msgid "M j, Y, G:i"
msgstr "j M, Y, G:i" msgstr "j M, Y, G:i"
#: judge/views/user.py:409 #: judge/views/user.py:417
msgid "Updated on site" msgid "Updated on site"
msgstr "Được cập nhật trên web" 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/admin/auth/user/change_form.html:17 templates/base.html:280
#: templates/user/user-tabs.html:11 #: templates/user/user-tabs.html:11
msgid "Edit profile" msgid "Edit profile"
msgstr "Chỉnh sửa thông tin" 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 #: templates/user/user-list-tabs.html:4
msgid "Leaderboard" msgid "Leaderboard"
msgstr "Xếp hạng" msgstr "Xếp hạng"
#: judge/views/user.py:574 #: judge/views/user.py:582
msgid "Import Users" msgid "Import Users"
msgstr "" msgstr ""

View file

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