diff --git a/dmoj/urls.py b/dmoj/urls.py
index aa77765..07f0f5c 100644
--- a/dmoj/urls.py
+++ b/dmoj/urls.py
@@ -523,6 +523,11 @@ urlpatterns = [
contests.ContestRanking.as_view(),
name="contest_ranking",
),
+ url(
+ r"^/final_ranking/$",
+ contests.ContestFinalRanking.as_view(),
+ name="contest_final_ranking",
+ ),
url(
r"^/ranking/ajax$",
contests.contest_ranking_ajax,
diff --git a/judge/contest_format/atcoder.py b/judge/contest_format/atcoder.py
index ee5b18d..65ceb90 100644
--- a/judge/contest_format/atcoder.py
+++ b/judge/contest_format/atcoder.py
@@ -112,7 +112,7 @@ class AtCoderContestFormat(DefaultContestFormat):
participation.format_data = format_data
participation.save()
- def display_user_problem(self, participation, contest_problem):
+ def display_user_problem(self, participation, contest_problem, show_final=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
penalty = (
diff --git a/judge/contest_format/base.py b/judge/contest_format/base.py
index 05c6391..78673bf 100644
--- a/judge/contest_format/base.py
+++ b/judge/contest_format/base.py
@@ -48,7 +48,7 @@ class BaseContestFormat(metaclass=ABCMeta):
raise NotImplementedError()
@abstractmethod
- def display_user_problem(self, participation, contest_problem):
+ def display_user_problem(self, participation, contest_problem, show_final):
"""
Returns the HTML fragment to show a user's performance on an individual problem. This is expected to use
information from the format_data field instead of computing it from scratch.
@@ -60,7 +60,7 @@ class BaseContestFormat(metaclass=ABCMeta):
raise NotImplementedError()
@abstractmethod
- def display_participation_result(self, participation):
+ def display_participation_result(self, participation, show_final):
"""
Returns the HTML fragment to show a user's performance on the whole contest. This is expected to use
information from the format_data field instead of computing it from scratch.
diff --git a/judge/contest_format/default.py b/judge/contest_format/default.py
index 69292e0..40fceb0 100644
--- a/judge/contest_format/default.py
+++ b/judge/contest_format/default.py
@@ -61,7 +61,7 @@ class DefaultContestFormat(BaseContestFormat):
participation.format_data = format_data
participation.save()
- def display_user_problem(self, participation, contest_problem):
+ def display_user_problem(self, participation, contest_problem, show_final=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
return format_html(
@@ -94,7 +94,7 @@ class DefaultContestFormat(BaseContestFormat):
else:
return mark_safe('
| ')
- def display_participation_result(self, participation):
+ def display_participation_result(self, participation, show_final=False):
return format_html(
'{points} {cumtime} | ',
points=floatformat(participation.score, -self.contest.points_precision),
diff --git a/judge/contest_format/ecoo.py b/judge/contest_format/ecoo.py
index f5429c7..c5c177d 100644
--- a/judge/contest_format/ecoo.py
+++ b/judge/contest_format/ecoo.py
@@ -122,7 +122,7 @@ class ECOOContestFormat(DefaultContestFormat):
participation.format_data = format_data
participation.save()
- def display_user_problem(self, participation, contest_problem):
+ def display_user_problem(self, participation, contest_problem, show_final=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
bonus = (
@@ -162,7 +162,7 @@ class ECOOContestFormat(DefaultContestFormat):
else:
return mark_safe(" | ")
- def display_participation_result(self, participation):
+ def display_participation_result(self, participation, show_final=False):
return format_html(
'{points} {cumtime} | ',
points=floatformat(participation.score),
diff --git a/judge/contest_format/icpc.py b/judge/contest_format/icpc.py
index 61495ae..bf0bfff 100644
--- a/judge/contest_format/icpc.py
+++ b/judge/contest_format/icpc.py
@@ -114,7 +114,7 @@ class ICPCContestFormat(DefaultContestFormat):
participation.format_data = format_data
participation.save()
- def display_user_problem(self, participation, contest_problem):
+ def display_user_problem(self, participation, contest_problem, show_final=False):
format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
penalty = (
diff --git a/judge/contest_format/ioi.py b/judge/contest_format/ioi.py
index 5c2502c..ce93252 100644
--- a/judge/contest_format/ioi.py
+++ b/judge/contest_format/ioi.py
@@ -86,8 +86,13 @@ class IOIContestFormat(DefaultContestFormat):
participation.format_data = format_data
participation.save()
- def display_user_problem(self, participation, contest_problem):
- format_data = (participation.format_data or {}).get(str(contest_problem.id))
+ def display_user_problem(self, participation, contest_problem, show_final=False):
+ if show_final:
+ format_data = (participation.format_data_final or {}).get(
+ str(contest_problem.id)
+ )
+ else:
+ format_data = (participation.format_data or {}).get(str(contest_problem.id))
if format_data:
return format_html(
'{points} {time} | ',
@@ -121,11 +126,17 @@ class IOIContestFormat(DefaultContestFormat):
else:
return mark_safe(' | ')
- def display_participation_result(self, participation):
+ def display_participation_result(self, participation, show_final=False):
+ if show_final:
+ score = participation.score_final
+ cumtime = participation.cumtime_final
+ else:
+ score = participation.score
+ cumtime = participation.cumtime
return format_html(
'{points} {cumtime} | ',
- points=floatformat(participation.score, -self.contest.points_precision),
- cumtime=nice_repr(timedelta(seconds=participation.cumtime), "noday")
+ points=floatformat(score, -self.contest.points_precision),
+ cumtime=nice_repr(timedelta(seconds=cumtime), "noday")
if self.config["cumtime"]
else "",
)
diff --git a/judge/contest_format/new_ioi.py b/judge/contest_format/new_ioi.py
index 3661d7e..73dcc84 100644
--- a/judge/contest_format/new_ioi.py
+++ b/judge/contest_format/new_ioi.py
@@ -100,51 +100,73 @@ class NewIOIContestFormat(IOIContestFormat):
return cursor.fetchall()
def update_participation(self, participation):
- cumtime = 0
- score = 0
- format_data = {}
frozen_subtasks = self.get_frozen_subtasks()
- for (
- problem_id,
- problem_points,
- time,
- subtask_points,
- total_subtask_points,
- subtask,
- sub_id,
- ) in self.get_results_by_subtask(participation):
- problem_id = str(problem_id)
- time = from_database_time(time)
- if self.config["cumtime"]:
- dt = (time - participation.start).total_seconds()
- else:
- dt = 0
+ def calculate_format_data(participation, include_frozen):
+ format_data = {}
+ for (
+ problem_id,
+ problem_points,
+ time,
+ subtask_points,
+ total_subtask_points,
+ subtask,
+ sub_id,
+ ) in self.get_results_by_subtask(participation, include_frozen):
+ problem_id = str(problem_id)
+ time = from_database_time(time)
+ if self.config["cumtime"]:
+ dt = (time - participation.start).total_seconds()
+ else:
+ dt = 0
- if format_data.get(problem_id) is None:
- format_data[problem_id] = {"points": 0, "time": 0, "total_points": 0}
- if subtask not in frozen_subtasks.get(problem_id, set()):
- format_data[problem_id]["points"] += subtask_points
- format_data[problem_id]["total_points"] += total_subtask_points
- format_data[problem_id]["time"] = max(dt, format_data[problem_id]["time"])
- format_data[problem_id]["problem_points"] = problem_points
+ if format_data.get(problem_id) is None:
+ format_data[problem_id] = {
+ "points": 0,
+ "time": 0,
+ "total_points": 0,
+ }
+ if (
+ subtask not in frozen_subtasks.get(problem_id, set())
+ or include_frozen
+ ):
+ format_data[problem_id]["points"] += subtask_points
+ format_data[problem_id]["total_points"] += total_subtask_points
+ format_data[problem_id]["time"] = max(
+ dt, format_data[problem_id]["time"]
+ )
+ format_data[problem_id]["problem_points"] = problem_points
- for problem_data in format_data.values():
- if not problem_data["total_points"]:
- continue
- penalty = problem_data["time"]
- problem_data["points"] = (
- problem_data["points"]
- / problem_data["total_points"]
- * problem_data["problem_points"]
- )
- if self.config["cumtime"] and problem_data["points"]:
- cumtime += penalty
- score += problem_data["points"]
+ return format_data
+ def recalculate_results(format_data):
+ cumtime = 0
+ score = 0
+ for problem_data in format_data.values():
+ if not problem_data["total_points"]:
+ continue
+ penalty = problem_data["time"]
+ problem_data["points"] = (
+ problem_data["points"]
+ / problem_data["total_points"]
+ * problem_data["problem_points"]
+ )
+ if self.config["cumtime"] and problem_data["points"]:
+ cumtime += penalty
+ score += problem_data["points"]
+ return score, cumtime
+
+ format_data = calculate_format_data(participation, False)
+ score, cumtime = recalculate_results(format_data)
self.handle_frozen_state(participation, format_data)
participation.cumtime = max(cumtime, 0)
participation.score = round(score, self.contest.points_precision)
participation.tiebreaker = 0
participation.format_data = format_data
+
+ format_data_final = calculate_format_data(participation, True)
+ score_final, cumtime_final = recalculate_results(format_data_final)
+ participation.cumtime_final = max(cumtime_final, 0)
+ participation.score_final = round(score_final, self.contest.points_precision)
+ participation.format_data_final = format_data_final
participation.save()
diff --git a/judge/migrations/0142_contestparticipation_format_data_final.py b/judge/migrations/0142_contestparticipation_format_data_final.py
new file mode 100644
index 0000000..50e8dd7
--- /dev/null
+++ b/judge/migrations/0142_contestparticipation_format_data_final.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.16 on 2022-12-28 18:36
+
+from django.db import migrations
+import jsonfield.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("judge", "0141_contestproblem_frozen_subtasks"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="contestparticipation",
+ name="format_data_final",
+ field=jsonfield.fields.JSONField(
+ blank=True,
+ null=True,
+ verbose_name="same as format_data, but including frozen results",
+ ),
+ ),
+ ]
diff --git a/judge/migrations/0143_auto_20221229_0153.py b/judge/migrations/0143_auto_20221229_0153.py
new file mode 100644
index 0000000..051f192
--- /dev/null
+++ b/judge/migrations/0143_auto_20221229_0153.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.2.16 on 2022-12-28 18:53
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("judge", "0142_contestparticipation_format_data_final"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="contestparticipation",
+ name="cumtime_final",
+ field=models.PositiveIntegerField(
+ default=0, verbose_name="final cumulative time"
+ ),
+ ),
+ migrations.AddField(
+ model_name="contestparticipation",
+ name="score_final",
+ field=models.FloatField(default=0, verbose_name="final score"),
+ ),
+ ]
diff --git a/judge/models/contest.py b/judge/models/contest.py
index 18f4c71..8c180bc 100644
--- a/judge/models/contest.py
+++ b/judge/models/contest.py
@@ -650,6 +650,15 @@ class ContestParticipation(models.Model):
format_data = JSONField(
verbose_name=_("contest format specific data"), null=True, blank=True
)
+ format_data_final = JSONField(
+ verbose_name=_("same as format_data, but including frozen results"),
+ null=True,
+ blank=True,
+ )
+ score_final = models.FloatField(verbose_name=_("final score"), default=0)
+ cumtime_final = models.PositiveIntegerField(
+ verbose_name=_("final cumulative time"), default=0
+ )
def recompute_results(self):
with transaction.atomic():
diff --git a/judge/views/contests.py b/judge/views/contests.py
index 8df20b2..1f400bb 100644
--- a/judge/views/contests.py
+++ b/judge/views/contests.py
@@ -877,48 +877,73 @@ ContestRankingProfile = namedtuple(
BestSolutionData = namedtuple("BestSolutionData", "code points time state is_pretested")
-def make_contest_ranking_profile(contest, participation, contest_problems):
+def make_contest_ranking_profile(
+ contest, participation, contest_problems, show_final=False
+):
+ if not show_final:
+ points = participation.score
+ cumtime = participation.cumtime
+ else:
+ points = participation.score_final
+ cumtime = participation.cumtime_final
+
user = participation.user
return ContestRankingProfile(
id=user.id,
user=user.user,
css_class=user.css_class,
username=user.username,
- points=participation.score,
- cumtime=participation.cumtime,
+ points=points,
+ cumtime=cumtime,
tiebreaker=participation.tiebreaker,
organization=user.organization,
participation_rating=participation.rating.rating
if hasattr(participation, "rating")
else None,
problem_cells=[
- contest.format.display_user_problem(participation, contest_problem)
+ contest.format.display_user_problem(
+ participation, contest_problem, show_final
+ )
for contest_problem in contest_problems
],
- result_cell=contest.format.display_participation_result(participation),
+ result_cell=contest.format.display_participation_result(
+ participation, show_final
+ ),
participation=participation,
)
-def base_contest_ranking_list(contest, problems, queryset):
+def base_contest_ranking_list(contest, problems, queryset, show_final=False):
return [
- make_contest_ranking_profile(contest, participation, problems)
+ make_contest_ranking_profile(contest, participation, problems, show_final)
for participation in queryset.select_related("user__user", "rating").defer(
"user__about", "user__organizations__about"
)
]
-def contest_ranking_list(contest, problems, queryset=None):
+def contest_ranking_list(contest, problems, queryset=None, show_final=False):
if not queryset:
queryset = contest.users.filter(virtual=0)
- return base_contest_ranking_list(
- contest,
- problems,
- queryset.prefetch_related("user__organizations")
- .extra(select={"round_score": "round(score, 6)"})
- .order_by("is_disqualified", "-round_score", "cumtime", "tiebreaker"),
- )
+
+ if not show_final:
+ return base_contest_ranking_list(
+ contest,
+ problems,
+ queryset.prefetch_related("user__organizations")
+ .extra(select={"round_score": "round(score, 6)"})
+ .order_by("is_disqualified", "-round_score", "cumtime", "tiebreaker"),
+ show_final,
+ )
+ else:
+ return base_contest_ranking_list(
+ contest,
+ problems,
+ queryset.prefetch_related("user__organizations")
+ .extra(select={"round_score": "round(score_final, 6)"})
+ .order_by("is_disqualified", "-round_score", "cumtime_final", "tiebreaker"),
+ show_final,
+ )
def get_contest_ranking_list(
@@ -928,6 +953,7 @@ def get_contest_ranking_list(
ranking_list=contest_ranking_list,
show_current_virtual=False,
ranker=ranker,
+ show_final=False,
):
problems = list(
contest.contest_problems.select_related("problem")
@@ -936,7 +962,7 @@ def get_contest_ranking_list(
)
users = ranker(
- ranking_list(contest, problems),
+ ranking_list(contest, problems, show_final=show_final),
key=attrgetter("points", "cumtime", "tiebreaker"),
)
@@ -955,12 +981,17 @@ def get_contest_ranking_list(
def contest_ranking_ajax(request, contest, participation=None):
contest, exists = _find_contest(request, contest)
+ show_final = bool(request.GET.get("final", False))
if not exists:
return HttpResponseBadRequest("Invalid contest", content_type="text/plain")
if not contest.can_see_full_scoreboard(request.user):
raise Http404()
+ if show_final:
+ if not request.user.is_superuser or contest.format_name != "ioi16":
+ raise Http404()
+
queryset = contest.users.filter(virtual__gte=0)
if request.GET.get("friend") == "true" and request.profile:
friends = list(request.profile.get_friends())
@@ -973,6 +1004,7 @@ def contest_ranking_ajax(request, contest, participation=None):
contest,
participation,
ranking_list=partial(contest_ranking_list, queryset=queryset),
+ show_final=show_final,
)
return render(
request,
@@ -1039,6 +1071,18 @@ class ContestRanking(ContestRankingBase):
return context
+class ContestFinalRanking(LoginRequiredMixin, ContestRanking):
+ page_type = "final_ranking"
+
+ def get_ranking_list(self):
+ if not self.request.user.is_superuser:
+ raise Http404()
+ if self.object.format_name != "ioi16":
+ raise Http404()
+
+ return get_contest_ranking_list(self.request, self.object, show_final=True)
+
+
class ContestParticipationList(LoginRequiredMixin, ContestRankingBase):
page_type = "participation"
diff --git a/locale/vi/LC_MESSAGES/django.po b/locale/vi/LC_MESSAGES/django.po
index 7191dd2..8a2c220 100644
--- a/locale/vi/LC_MESSAGES/django.po
+++ b/locale/vi/LC_MESSAGES/django.po
@@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: lqdoj2\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2022-12-20 15:25+0700\n"
+"POT-Creation-Date: 2022-12-29 03:29+0700\n"
"PO-Revision-Date: 2021-07-20 03:44\n"
"Last-Translator: Icyene\n"
"Language-Team: Vietnamese\n"
@@ -20,7 +20,7 @@ msgstr ""
#: chat_box/models.py:31 chat_box/models.py:51 chat_box/models.py:65
#: judge/admin/interface.py:150 judge/models/contest.py:630
-#: judge/models/contest.py:830 judge/models/profile.py:354
+#: judge/models/contest.py:835 judge/models/profile.py:354
#: judge/models/profile.py:430
msgid "user"
msgstr "người dùng"
@@ -41,11 +41,11 @@ msgstr "xem lần cuối"
msgid "Chat Box"
msgstr "Chat Box"
-#: dmoj/settings.py:362
+#: dmoj/settings.py:360
msgid "Vietnamese"
msgstr "Tiếng Việt"
-#: dmoj/settings.py:363
+#: dmoj/settings.py:361
msgid "English"
msgstr ""
@@ -53,7 +53,7 @@ msgstr ""
msgid "Login"
msgstr "Đăng nhập"
-#: dmoj/urls.py:211 templates/base.html:207
+#: dmoj/urls.py:211 templates/base.html:208
#: templates/organization/org-left-sidebar.html:2
msgid "Home"
msgstr "Trang chủ"
@@ -164,7 +164,7 @@ msgstr "Tính toán lại kết quả"
msgid "username"
msgstr "tên đăng nhập"
-#: judge/admin/contest.py:496 templates/base.html:315
+#: judge/admin/contest.py:496 templates/base.html:316
msgid "virtual"
msgstr "ảo"
@@ -226,7 +226,7 @@ msgid "Limits"
msgstr "Giới hạn"
#: judge/admin/problem.py:217 judge/admin/submission.py:353
-#: templates/base.html:243 templates/stats/tab.html:4
+#: templates/base.html:244 templates/stats/tab.html:4
#: templates/submission/list.html:342
msgid "Language"
msgstr "Ngôn ngữ"
@@ -301,7 +301,7 @@ msgstr "Thành viên"
msgid "Email"
msgstr "Email"
-#: judge/admin/profile.py:137 judge/views/register.py:37
+#: judge/admin/profile.py:137 judge/views/register.py:36
#: templates/registration/registration_form.html:68
#: templates/user/edit-profile.html:113
msgid "Timezone"
@@ -467,75 +467,67 @@ msgstr ""
msgid "IOI"
msgstr ""
-#: judge/contest_format/new_ioi.py:11
+#: judge/contest_format/new_ioi.py:13
msgid "New IOI"
msgstr "IOI mới"
-#: judge/forms.py:70
-msgid "Subscribe to contest updates"
-msgstr "Đăng ký để nhận thông báo về các kỳ thi"
-
-#: judge/forms.py:73
-msgid "Enable experimental features"
-msgstr "Sử dụng các tính năng thử nghiệm"
-
-#: judge/forms.py:110 judge/views/organization.py:536
-#: judge/views/register.py:68
+#: judge/forms.py:102 judge/views/organization.py:536
+#: judge/views/register.py:62
#, python-brace-format
msgid "You may not be part of more than {count} public groups."
msgstr "Bạn không thể tham gia nhiều hơn {count} nhóm công khai."
-#: judge/forms.py:142
+#: judge/forms.py:134
msgid "Any judge"
msgstr ""
-#: judge/forms.py:317
+#: judge/forms.py:309
msgid "Enter usernames separating by space"
msgstr "Nhập các tên đăng nhập, cách nhau bởi dấu cách"
-#: judge/forms.py:318 judge/views/stats.py:166 templates/stats/site.html:27
+#: judge/forms.py:310 judge/views/stats.py:166 templates/stats/site.html:27
msgid "New users"
msgstr "Thành viên mới"
-#: judge/forms.py:335
+#: judge/forms.py:327
#, python-brace-format
msgid "These usernames don't exist: {usernames}"
msgstr "Các tên đăng nhập này không tồn tại: {usernames}"
-#: judge/forms.py:394 judge/views/register.py:31
+#: judge/forms.py:386 judge/views/register.py:30
#: templates/registration/registration_form.html:34
#: templates/user/base-users-table.html:5
#: templates/user/import/table_csv.html:4
msgid "Username"
msgstr "Tên đăng nhập"
-#: judge/forms.py:395 templates/registration/registration_form.html:46
+#: judge/forms.py:387 templates/registration/registration_form.html:46
#: templates/registration/registration_form.html:60
#: templates/user/import/table_csv.html:5
msgid "Password"
msgstr "Mật khẩu"
-#: judge/forms.py:421
+#: judge/forms.py:413
msgid "Two Factor Authentication tokens must be 6 decimal digits."
msgstr "Two Factor Authentication phải chứa 6 chữ số."
-#: judge/forms.py:434 templates/registration/totp_auth.html:32
+#: judge/forms.py:426 templates/registration/totp_auth.html:32
msgid "Invalid Two Factor Authentication token."
msgstr "Token Two Factor Authentication không hợp lệ."
-#: judge/forms.py:441 judge/models/problem.py:130
+#: judge/forms.py:433 judge/models/problem.py:130
msgid "Problem code must be ^[a-z0-9]+$"
msgstr "Mã bài phải có dạng ^[a-z0-9]+$"
-#: judge/forms.py:448
+#: judge/forms.py:440
msgid "Problem with code already exists."
msgstr "Mã bài đã tồn tại."
-#: judge/forms.py:455 judge/models/contest.py:90
+#: judge/forms.py:447 judge/models/contest.py:90
msgid "Contest id must be ^[a-z0-9]+$"
msgstr "Mã kỳ thi phải có dạng ^[a-z0-9]+$"
-#: judge/forms.py:461
+#: judge/forms.py:453
msgid "Contest with key already exists."
msgstr "Mã kỳ thi đã tồn tại."
@@ -1011,8 +1003,8 @@ msgstr ""
msgid "Edit contest problem label script"
msgstr "Cách hiển thị thứ tự bài tập"
-#: judge/models/contest.py:614 judge/models/contest.py:752
-#: judge/models/contest.py:833 judge/models/contest.py:863
+#: judge/models/contest.py:614 judge/models/contest.py:757
+#: judge/models/contest.py:838 judge/models/contest.py:868
#: judge/models/submission.py:116
msgid "contest"
msgstr "kỳ thi"
@@ -1057,154 +1049,171 @@ msgstr "0 nghĩa là tham gia chính thức, ngược lại là lần tham gia
msgid "contest format specific data"
msgstr ""
-#: judge/models/contest.py:727
+#: judge/models/contest.py:654
+msgid "same as format_data, but including frozen results"
+msgstr ""
+
+#: judge/models/contest.py:656
+#, fuzzy
+#| msgid "score"
+msgid "final score"
+msgstr "điểm"
+
+#: judge/models/contest.py:657
+#, fuzzy
+#| msgid "cumulative time"
+msgid "final cumulative time"
+msgstr "tổng thời gian"
+
+#: judge/models/contest.py:732
#, python-format
msgid "%s spectating in %s"
msgstr "%s đang theo dõi trong %s"
-#: judge/models/contest.py:732
+#: judge/models/contest.py:737
#, python-format
msgid "%s in %s, v%d"
msgstr "%s trong %s, v%d"
-#: judge/models/contest.py:737
+#: judge/models/contest.py:742
#, python-format
msgid "%s in %s"
msgstr "%s trong %s"
-#: judge/models/contest.py:740
+#: judge/models/contest.py:745
msgid "contest participation"
msgstr "lần tham gia kỳ thi"
-#: judge/models/contest.py:741
+#: judge/models/contest.py:746
msgid "contest participations"
msgstr "lần tham gia kỳ thi"
-#: judge/models/contest.py:748 judge/models/contest.py:804
-#: judge/models/contest.py:866 judge/models/problem.py:558
+#: judge/models/contest.py:753 judge/models/contest.py:809
+#: judge/models/contest.py:871 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:756 judge/models/contest.py:816
+#: judge/models/contest.py:761 judge/models/contest.py:821
#: judge/models/problem.py:206
msgid "points"
msgstr "điểm"
-#: judge/models/contest.py:757
+#: judge/models/contest.py:762
msgid "partial"
msgstr "thành phần"
-#: judge/models/contest.py:758 judge/models/contest.py:818
+#: judge/models/contest.py:763 judge/models/contest.py:823
msgid "is pretested"
msgstr "dùng pretest"
-#: judge/models/contest.py:759 judge/models/interface.py:43
+#: judge/models/contest.py:764 judge/models/interface.py:43
msgid "order"
msgstr "thứ tự"
-#: judge/models/contest.py:761
+#: judge/models/contest.py:766
msgid "0 to not show testcases, 1 to show"
msgstr "0 để ẩn test, 1 để hiện"
-#: judge/models/contest.py:762
+#: judge/models/contest.py:767
msgid "visible testcases"
msgstr "hiển thị test"
-#: judge/models/contest.py:769
+#: judge/models/contest.py:774
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:771
+#: judge/models/contest.py:776
msgid "max submissions"
msgstr "số lần nộp tối đa"
-#: judge/models/contest.py:774
+#: judge/models/contest.py:779
msgid "Why include a problem you can't submit to?"
msgstr ""
-#: judge/models/contest.py:778
+#: judge/models/contest.py:783
msgid "Only for format new IOI. Separated by commas, e.g: 2, 3"
-msgstr "Chỉ dùng với format IOI mới. Các sub cách nhau bởi dấu phẩy. Ví dụ: 2, 3"
+msgstr ""
+"Chỉ dùng với format IOI mới. Các sub cách nhau bởi dấu phẩy. Ví dụ: 2, 3"
-#: judge/models/contest.py:779
+#: judge/models/contest.py:784
msgid "frozen subtasks"
msgstr "Đóng băng subtasks"
-#: judge/models/contest.py:791
+#: judge/models/contest.py:796
msgid "contest problem"
msgstr "bài trong kỳ thi"
-#: judge/models/contest.py:792
+#: judge/models/contest.py:797
msgid "contest problems"
msgstr "bài trong kỳ thi"
-#: judge/models/contest.py:798 judge/models/submission.py:233
+#: judge/models/contest.py:803 judge/models/submission.py:233
msgid "submission"
msgstr "bài nộp"
-#: judge/models/contest.py:811 judge/models/contest.py:837
+#: judge/models/contest.py:816 judge/models/contest.py:842
msgid "participation"
msgstr "lần tham gia"
-#: judge/models/contest.py:819
+#: judge/models/contest.py:824
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:824
+#: judge/models/contest.py:829
msgid "contest submission"
msgstr "bài nộp kỳ thi"
-#: judge/models/contest.py:825
+#: judge/models/contest.py:830
msgid "contest submissions"
msgstr "bài nộp kỳ thi"
-#: judge/models/contest.py:841
+#: judge/models/contest.py:846
msgid "rank"
msgstr "rank"
-#: judge/models/contest.py:842
+#: judge/models/contest.py:847
msgid "rating"
msgstr "rating"
-#: judge/models/contest.py:843
+#: judge/models/contest.py:848
msgid "raw rating"
msgstr "rating thật"
-#: judge/models/contest.py:844
+#: judge/models/contest.py:849
msgid "contest performance"
msgstr ""
-#: judge/models/contest.py:845
+#: judge/models/contest.py:850
msgid "last rated"
msgstr "lần cuối được xếp hạng"
-#: judge/models/contest.py:849
+#: judge/models/contest.py:854
msgid "contest rating"
msgstr "rating kỳ thi"
-#: judge/models/contest.py:850
+#: judge/models/contest.py:855
msgid "contest ratings"
msgstr "rating kỳ thi"
-#: judge/models/contest.py:874
+#: judge/models/contest.py:879
msgid "contest moss result"
msgstr "kết quả MOSS kỳ thi"
-#: judge/models/contest.py:875
+#: judge/models/contest.py:880
msgid "contest moss results"
msgstr "kết quả MOSS kỳ thi"
-#: judge/models/contest.py:880
+#: judge/models/contest.py:885
msgid "clarified problem"
msgstr ""
-#: judge/models/contest.py:882
+#: judge/models/contest.py:887
msgid "clarification body"
msgstr ""
-#: judge/models/contest.py:884
+#: judge/models/contest.py:889
msgid "clarification timestamp"
msgstr ""
@@ -2406,6 +2415,20 @@ msgstr "vote từ TNV"
msgid "volunteer votes"
msgstr "vote từ TNV"
+#: judge/models/xmas.py:13
+#, fuzzy
+#| msgid "Default"
+msgid "default"
+msgstr "Mặc định"
+
+#: judge/models/xmas.py:24
+msgid "use snowy background"
+msgstr ""
+
+#: judge/models/xmas.py:31
+msgid "user changed username"
+msgstr ""
+
#: judge/pdf_problems.py:161 judge/pdf_problems.py:221
#: judge/pdf_problems.py:294
msgid "Page [page] of [topage]"
@@ -2568,21 +2591,21 @@ msgstr "Bạn phải giải ít nhất 1 bài trước khi được vote."
msgid "You already voted."
msgstr "Bạn đã vote."
-#: judge/views/comment.py:155 judge/views/organization.py:836
+#: judge/views/comment.py:158 judge/views/organization.py:836
#: judge/views/organization.py:982 judge/views/organization.py:1147
msgid "Edited from site"
msgstr "Chỉnh sửa từ web"
-#: judge/views/comment.py:176
+#: judge/views/comment.py:179
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:619
+#: judge/views/contests.py:119 judge/views/contests.py:368
+#: judge/views/contests.py:373 judge/views/contests.py:620
msgid "No such contest"
msgstr "Không có contest nào như vậy"
-#: judge/views/contests.py:120 judge/views/contests.py:368
+#: judge/views/contests.py:120 judge/views/contests.py:369
#, python-format
msgid "Could not find a contest with the key \"%s\"."
msgstr "Không tìm thấy kỳ thi với mã \"%s\"."
@@ -2593,122 +2616,122 @@ msgstr "Không tìm thấy kỳ thi với mã \"%s\"."
msgid "Contests"
msgstr "Kỳ thi"
-#: judge/views/contests.py:372
+#: judge/views/contests.py:373
msgid "Could not find such contest."
msgstr "Không tìm thấy kỳ thi nào như vậy."
-#: judge/views/contests.py:380
+#: judge/views/contests.py:381
#, python-format
msgid "Access to contest \"%s\" denied"
msgstr "Truy cập tới kỳ thi \"%s\" bị từ chối"
-#: judge/views/contests.py:424
+#: judge/views/contests.py:425
msgid "Clone Contest"
msgstr "Nhân bản kỳ thi"
-#: judge/views/contests.py:493
+#: judge/views/contests.py:494
msgid "Contest not ongoing"
msgstr "Kỳ thi đang không diễn ra"
-#: judge/views/contests.py:494
+#: judge/views/contests.py:495
#, python-format
msgid "\"%s\" is not currently ongoing."
msgstr "\"%s\" kỳ thi đang không diễn ra."
-#: judge/views/contests.py:501
+#: judge/views/contests.py:502
msgid "Already in contest"
msgstr "Đã ở trong kỳ thi"
-#: judge/views/contests.py:502
+#: judge/views/contests.py:503
#, python-format
msgid "You are already in a contest: \"%s\"."
msgstr "Bạn đã ở trong kỳ thi: \"%s\"."
-#: judge/views/contests.py:512
+#: judge/views/contests.py:513
msgid "Banned from joining"
msgstr "Bị cấm tham gia"
-#: judge/views/contests.py:514
+#: judge/views/contests.py:515
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:603
+#: judge/views/contests.py:604
#, python-format
msgid "Enter access code for \"%s\""
msgstr "Nhập mật khẩu truy cập cho \"%s\""
-#: judge/views/contests.py:620
+#: judge/views/contests.py:621
#, python-format
msgid "You are not in contest \"%s\"."
msgstr "Bạn không ở trong kỳ thi \"%s\"."
-#: judge/views/contests.py:643
+#: judge/views/contests.py:644
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:701
+#: judge/views/contests.py:702
#, python-format
msgid "Contests in %(month)s"
msgstr "Các kỳ thi trong %(month)s"
-#: judge/views/contests.py:702
+#: judge/views/contests.py:703
msgid "F Y"
msgstr "F Y"
-#: judge/views/contests.py:762
+#: judge/views/contests.py:763
#, python-format
msgid "%s Statistics"
msgstr "%s Thống kê"
-#: judge/views/contests.py:1019
+#: judge/views/contests.py:1046
#, python-format
msgid "%s Rankings"
msgstr "%s Bảng điểm"
-#: judge/views/contests.py:1030
+#: judge/views/contests.py:1057
msgid "???"
msgstr "???"
-#: judge/views/contests.py:1046
+#: judge/views/contests.py:1085
#, python-format
msgid "Your participation in %s"
msgstr "Lần tham gia trong %s"
-#: judge/views/contests.py:1047
+#: judge/views/contests.py:1086
#, python-format
msgid "%s's participation in %s"
msgstr "Lần tham gia của %s trong %s"
-#: judge/views/contests.py:1061
+#: judge/views/contests.py:1100
msgid "Live"
msgstr "Trực tiếp"
-#: judge/views/contests.py:1080 templates/contest/contest-tabs.html:19
+#: judge/views/contests.py:1119 templates/contest/contest-tabs.html:19
msgid "Participation"
msgstr "Lần tham gia"
-#: judge/views/contests.py:1129
+#: judge/views/contests.py:1168
#, python-format
msgid "%s MOSS Results"
msgstr "%s Kết quả MOSS"
-#: judge/views/contests.py:1165
+#: judge/views/contests.py:1204
#, python-format
msgid "Running MOSS for %s..."
msgstr "Đang chạy MOSS cho %s..."
-#: judge/views/contests.py:1188
+#: judge/views/contests.py:1227
#, python-format
msgid "Contest tag: %s"
msgstr "Nhãn kỳ thi: %s"
-#: judge/views/contests.py:1203 judge/views/ticket.py:72
+#: judge/views/contests.py:1242 judge/views/ticket.py:72
msgid "Issue description"
msgstr "Mô tả vấn đề"
-#: judge/views/contests.py:1250
+#: judge/views/contests.py:1289
#, python-format
msgid "New clarification for %s"
msgstr "Thông báo mới cho %s"
@@ -2779,7 +2802,7 @@ msgstr "Không thể chỉnh sửa tổ chức"
msgid "You are not allowed to access this organization."
msgstr "Bạn không được phép chỉnh sửa tổ chức này."
-#: judge/views/organization.py:245 judge/views/register.py:49
+#: judge/views/organization.py:245 judge/views/register.py:48
#: judge/views/stats.py:184 templates/contest/list.html:85
#: templates/problem/list-base.html:98 templates/stats/site.html:33
#: templates/user/user-left-sidebar.html:4 templates/user/user-list-tabs.html:6
@@ -3082,19 +3105,15 @@ msgstr ""
msgid "Best solutions for problem {0} in {1}"
msgstr "Các bài nộp tốt nhất cho bài {0} trong {1}"
-#: judge/views/register.py:33
+#: judge/views/register.py:32
msgid "A username must contain letters, numbers, or underscores"
msgstr "Tên đăng nhập phải chứa ký tự, chữ số, hoặc dấu gạch dưới"
-#: judge/views/register.py:43 templates/user/edit-profile.html:117
+#: judge/views/register.py:42 templates/user/edit-profile.html:117
msgid "Preferred language"
msgstr "Ngôn ngữ ưa thích"
-#: judge/views/register.py:56
-msgid "Subscribe to newsletter?"
-msgstr "Đăng ký để nhận thông báo?"
-
-#: judge/views/register.py:79
+#: judge/views/register.py:73
#, python-format
msgid ""
"The email address \"%s\" is already taken. Only one registration is allowed "
@@ -3102,7 +3121,7 @@ msgid ""
msgstr ""
"Email \"%s\" đã được sử dụng. Mỗi email chỉ có thể đăng ký một tài khoản."
-#: judge/views/register.py:91
+#: judge/views/register.py:85
msgid ""
"Your email provider is not allowed due to history of abuse. Please use a "
"reputable email provider."
@@ -3110,15 +3129,15 @@ msgstr ""
"Your email provider is not allowed due to history of abuse. Please use a "
"reputable email provider."
-#: judge/views/register.py:99 judge/views/register.py:140
+#: judge/views/register.py:93 judge/views/register.py:131
msgid "Registration"
msgstr "Đăng ký"
-#: judge/views/register.py:154
+#: judge/views/register.py:145
msgid "Authentication failure"
msgstr "Xác thực thất bại"
-#: judge/views/resolver.py:7 templates/contest/contest-tabs.html:26
+#: judge/views/resolver.py:11 templates/contest/contest-tabs.html:26
#, fuzzy
#| msgid "solve rate"
msgid "Resolver"
@@ -3168,39 +3187,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:512
+#: judge/views/submission.py:518
msgid "All my submissions"
msgstr "Tất cả bài nộp của tôi"
-#: judge/views/submission.py:513
+#: judge/views/submission.py:519
#, python-format
msgid "All submissions by %s"
msgstr "Tất cả bài nộp của %s"
-#: judge/views/submission.py:558
+#: judge/views/submission.py:564
#, python-format
msgid "All submissions for %s"
msgstr "Tất cả bài nộp cho %s"
-#: judge/views/submission.py:586
+#: judge/views/submission.py:592
msgid "Must pass a problem"
msgstr "Phải làm được một bài"
-#: judge/views/submission.py:644
+#: judge/views/submission.py:650
#, python-format
msgid "My submissions for %(problem)s"
msgstr "Bài nộp của tôi cho %(problem)s"
-#: judge/views/submission.py:645
+#: judge/views/submission.py:651
#, 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:775
+#: judge/views/submission.py:781
msgid "Must pass a contest"
msgstr "Phải qua một kỳ thi"
-#: judge/views/submission.py:805
+#: judge/views/submission.py:811
#, python-brace-format
msgid ""
"{0}'s submissions for {2} in {0} cho {2} trong {4}"
-#: judge/views/submission.py:817
+#: judge/views/submission.py:823
#, python-brace-format
msgid ""
"{0}'s submissions for problem {2} in {3}"
@@ -3218,7 +3237,7 @@ msgstr ""
"Các bài nộp của {0} cho bài {2} trong {3}"
""
-#: judge/views/submission.py:945
+#: judge/views/submission.py:961
#, fuzzy
#| msgid "You do not have the permission to rejudge submissions."
msgid "You don't have permission to access."
@@ -3278,48 +3297,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:97
+#: judge/views/user.py:96
msgid "No such user"
msgstr "Không người dùng nào như vậy"
-#: judge/views/user.py:98
+#: judge/views/user.py:97
#, 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:103
+#: judge/views/user.py:102
msgid "My account"
msgstr "Tài khoản của tôi"
-#: judge/views/user.py:105
+#: judge/views/user.py:104
#, python-format
msgid "User %s"
msgstr "Thành viên %s"
-#: judge/views/user.py:203
+#: judge/views/user.py:202
msgid "M j, Y"
msgstr "j M, Y"
-#: judge/views/user.py:238
+#: judge/views/user.py:237
msgid "M j, Y, G:i"
msgstr "j M, Y, G:i"
-#: judge/views/user.py:417
+#: judge/views/user.py:416
msgid "Updated on site"
msgstr "Được cập nhật trên web"
-#: 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
+#: judge/views/user.py:431 templates/admin/auth/user/change_form.html:14
+#: templates/admin/auth/user/change_form.html:17 templates/base.html:281
#: templates/user/user-tabs.html:11
msgid "Edit profile"
msgstr "Chỉnh sửa thông tin"
-#: judge/views/user.py:483 templates/user/user-left-sidebar.html:2
+#: judge/views/user.py:442 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:582
+#: judge/views/user.py:541
msgid "Import Users"
msgstr ""
@@ -3541,23 +3560,23 @@ msgstr "Chỉnh sửa thông tin"
msgid "Rejudge"
msgstr "Chấm lại"
-#: templates/base.html:225 templates/chat/chat.html:580
+#: templates/base.html:226 templates/chat/chat.html:580
msgid "Chat"
msgstr "Chat"
-#: templates/base.html:235
+#: templates/base.html:236
msgid "Notification"
msgstr "Thông báo"
-#: templates/base.html:256
+#: templates/base.html:257
msgid "Dark Mode"
msgstr ""
-#: templates/base.html:272
+#: templates/base.html:273
msgid "Profile"
msgstr "Trang cá nhân"
-#: templates/base.html:274 templates/chat/chat.html:20
+#: templates/base.html:275 templates/chat/chat.html:20
#: templates/comments/list.html:125 templates/contest/contest-list-tabs.html:4
#: templates/contest/ranking-table.html:49 templates/internal/base.html:59
#: templates/organization/org-left-sidebar.html:12
@@ -3568,44 +3587,44 @@ msgstr "Trang cá nhân"
msgid "Admin"
msgstr "Admin"
-#: templates/base.html:277
+#: templates/base.html:278
msgid "Internal"
msgstr "Nội bộ"
-#: templates/base.html:278
+#: templates/base.html:279
msgid "Stats"
msgstr "Thống kê"
-#: templates/base.html:285
+#: templates/base.html:286
msgid "Log out"
msgstr "Đăng xuất"
-#: templates/base.html:294
+#: templates/base.html:295
#: templates/registration/password_reset_complete.html:4
msgid "Log in"
msgstr "Đăng nhập"
-#: templates/base.html:295 templates/registration/registration_form.html:72
+#: templates/base.html:296 templates/registration/registration_form.html:72
msgid "or"
msgstr "hoặc"
-#: templates/base.html:296
+#: templates/base.html:297
msgid "Sign up"
msgstr "Đăng ký"
-#: templates/base.html:309
+#: templates/base.html:310
msgid "spectating"
msgstr "đang theo dõi"
-#: templates/base.html:321
+#: templates/base.html:322
msgid "Compete"
msgstr "Thi"
-#: templates/base.html:323
+#: templates/base.html:324
msgid "General"
msgstr "Chung"
-#: templates/base.html:330
+#: templates/base.html:331
msgid "This site works best with JavaScript enabled."
msgstr ""
@@ -3615,7 +3634,7 @@ msgid " posted on %(time)s"
msgstr "đã đăng vào %(time)s"
#: templates/blog/blog.html:32 templates/comments/list.html:104
-#: templates/comments/list.html:119 templates/contest/contest-tabs.html:32
+#: templates/comments/list.html:119 templates/contest/contest-tabs.html:33
#: templates/contest/list.html:120 templates/contest/tag-title.html:9
#: templates/flatpages/admin_link.html:3 templates/license.html:10
#: templates/problem/editorial.html:16 templates/problem/feed.html:78
@@ -3766,7 +3785,7 @@ msgstr "bình luận vào {time}"
msgid "edit %(edits)s"
msgstr "chỉnh sửa %(edits)s"
-#: templates/comments/list.html:87 templates/comments/media-js.html:89
+#: templates/comments/list.html:87 templates/comments/media-js.html:96
msgid "edited"
msgstr "đã chỉnh sửa"
@@ -3807,16 +3826,16 @@ msgstr "Không có bình luận nào."
msgid "Comments are disabled on this page."
msgstr "Bình luận bị tắt trong trang này."
-#: templates/comments/media-js.html:38
+#: templates/comments/media-js.html:39
msgid "Replying to comment"
msgstr "Trả lời bình luận"
-#: templates/comments/media-js.html:84
+#: templates/comments/media-js.html:91
#, python-brace-format
msgid "edit {edits}"
msgstr "chỉnh sửa {edits}"
-#: templates/comments/media-js.html:87
+#: templates/comments/media-js.html:94
msgid "original"
msgstr "original"
@@ -3976,11 +3995,15 @@ msgstr "Bảng xếp hạng"
msgid "Hidden Rankings"
msgstr "Bảng xếp hạng ẩn"
-#: templates/contest/contest-tabs.html:30
+#: templates/contest/contest-tabs.html:27
+msgid "Final rankings"
+msgstr "BXH chung cuộc"
+
+#: templates/contest/contest-tabs.html:31
msgid "MOSS"
msgstr "MOSS"
-#: templates/contest/contest-tabs.html:35
+#: templates/contest/contest-tabs.html:36
msgid "Clone"
msgstr "Nhân bản"
@@ -4023,7 +4046,7 @@ msgstr "Người nộp"
msgid "Editorial"
msgstr "Hướng dẫn"
-#: templates/contest/list.html:81 templates/contest/media-js.html:148
+#: templates/contest/list.html:81 templates/contest/media-js.html:152
msgid "Are you sure you want to join?"
msgstr "Bạn có chắc tham gia?"
@@ -4108,11 +4131,11 @@ msgstr "Không có kỳ thi nào được lên lịch hiện tại."
msgid "Past Contests"
msgstr "Kỳ thi trong quá khứ"
-#: templates/contest/media-js.html:143
+#: templates/contest/media-js.html:147
msgid "Are you sure you want to leave?"
msgstr "Bạn có chắc muốn rời?"
-#: templates/contest/media-js.html:144
+#: templates/contest/media-js.html:148
msgid ""
"You cannot come back to a virtual participation. You will have to start a "
"new one."
@@ -4120,7 +4143,7 @@ msgstr ""
"Bạn không thể quay lại lần tham gia ảo này. Bạn sẽ phải tham gia ảo lại từ "
"đầu."
-#: templates/contest/media-js.html:149
+#: templates/contest/media-js.html:153
msgid ""
"Joining a contest starts your timer, after which it becomes unstoppable."
msgstr "Tham gia kỳ thi sẽ khởi động đồng hồ đếm ngược, và không thể dừng lại."
@@ -4302,93 +4325,6 @@ msgstr "Gợi ý"
msgid "Source:"
msgstr "Nguồn:"
-#: templates/newsletter/common.html:6
-#: templates/newsletter/newsletter_list.html:15
-#: templates/newsletter/subscription_unsubscribe_activated.html:3
-#: templates/newsletter/subscription_unsubscribe_activated.html:6
-#: templates/newsletter/subscription_update_activated.html:3
-#: templates/newsletter/subscription_update_activated.html:6
-msgid "Newsletter"
-msgstr ""
-
-#: templates/newsletter/newsletter_list.html:2
-#: templates/newsletter/newsletter_list.html:3
-msgid "Newsletter list"
-msgstr ""
-
-#: templates/newsletter/newsletter_list.html:6
-msgid "Subscribe to get the latest emails about upcoming contests and events."
-msgstr ""
-
-#: templates/newsletter/newsletter_list.html:16
-msgid "Subscribe"
-msgstr ""
-
-#: templates/newsletter/newsletter_list.html:30
-msgid "Update subscriptions"
-msgstr ""
-
-#: templates/newsletter/subscription_unsubscribe_activated.html:3
-#: templates/newsletter/subscription_unsubscribe_activated.html:6
-#: templates/newsletter/subscription_update_activated.html:3
-#: templates/newsletter/subscription_update_activated.html:6
-msgid "activate"
-msgstr ""
-
-#: templates/newsletter/subscription_unsubscribe_activated.html:8
-msgid "You have successfully been unsubscribed."
-msgstr ""
-
-#: templates/newsletter/subscription_unsubscribe_email_sent.html:3
-#: templates/newsletter/subscription_unsubscribe_email_sent.html:6
-#: templates/newsletter/subscription_unsubscribe_user.html:3
-#: templates/newsletter/subscription_unsubscribe_user.html:6
-msgid "Newsletter unsubscribe"
-msgstr ""
-
-#: templates/newsletter/subscription_unsubscribe_email_sent.html:8
-msgid ""
-"Your unsubscription request has successfully been received. An email has "
-"been sent to you with a link you need to follow in order to confirm your "
-"unsubscription."
-msgstr ""
-
-#: templates/newsletter/subscription_unsubscribe_user.html:17
-msgid "Do you want to unsubscribe from this newsletter?"
-msgstr ""
-
-#: templates/newsletter/subscription_unsubscribe_user.html:21
-msgid "Unsubscribe"
-msgstr ""
-
-#: templates/newsletter/subscription_update.html:3
-#: templates/newsletter/subscription_update.html:6
-#: templates/newsletter/subscription_update_email_sent.html:3
-#: templates/newsletter/subscription_update_email_sent.html:6
-msgid "Newsletter update"
-msgstr ""
-
-#: templates/newsletter/subscription_update.html:9
-msgid ""
-"Due to a technical error we were not able to submit your confirmation email. "
-"This could be because your email address is invalid."
-msgstr ""
-
-#: templates/newsletter/subscription_update.html:14
-msgid "Update subscription"
-msgstr ""
-
-#: templates/newsletter/subscription_update_activated.html:8
-msgid "Your subscription has successfully been updated."
-msgstr ""
-
-#: templates/newsletter/subscription_update_email_sent.html:8
-msgid ""
-"Your update request was successfully received and an activation email has "
-"been sent to you. In that email you will find a link which you need to "
-"follow in order to update your subscription."
-msgstr ""
-
#: templates/notification/list.html:7
msgid "You have no notifications"
msgstr "Bạn không có thông báo"
@@ -5155,24 +5091,19 @@ msgid "Default language"
msgstr "Ngôn ngữ ưa thích"
#: templates/registration/registration_form.html:81
-#: templates/user/edit-profile.html:165
+#: templates/user/edit-profile.html:147
msgid "Affiliated organizations"
msgstr "Tổ chức bạn muốn tham gia"
-#: templates/registration/registration_form.html:90
-#: templates/user/edit-profile.html:135
-msgid "Notify me about upcoming contests"
-msgstr "Nhận thông báo về các kỳ thi tương lai"
-
-#: templates/registration/registration_form.html:104
+#: templates/registration/registration_form.html:97
msgid "By registering, you agree to our"
msgstr "Bạn đồng ý với"
-#: templates/registration/registration_form.html:105
+#: templates/registration/registration_form.html:98
msgid "Terms & Conditions"
msgstr "Điều khoản của chúng tôi"
-#: templates/registration/registration_form.html:108
+#: templates/registration/registration_form.html:101
msgid "Register!"
msgstr "Đăng ký!"
@@ -5442,11 +5373,11 @@ msgstr "Subtask"
msgid "Total"
msgstr "Tổng điểm"
-#: templates/submission/user-ajax.html:46
+#: templates/submission/user-ajax.html:47
msgid "g:i a d/m/Y"
msgstr ""
-#: templates/submission/user-ajax.html:46
+#: templates/submission/user-ajax.html:47
#, fuzzy, python-format
#| msgid ""
#| "\n"
@@ -5461,11 +5392,11 @@ msgstr ""
" vào %(time)s\n"
" "
-#: templates/submission/user-ajax.html:55
+#: templates/submission/user-ajax.html:56
msgid "pretests"
msgstr "pretests"
-#: templates/submission/user-ajax.html:57
+#: templates/submission/user-ajax.html:58
msgid "main tests"
msgstr "test chính thức"
@@ -5574,27 +5505,27 @@ msgstr "Giao diện cho code editor"
msgid "Math engine"
msgstr ""
-#: templates/user/edit-profile.html:150 templates/user/edit-profile.html:151
+#: templates/user/edit-profile.html:132 templates/user/edit-profile.html:133
msgid "Change your avatar"
msgstr "Đổi ảnh đại diện"
-#: templates/user/edit-profile.html:157
+#: templates/user/edit-profile.html:139
msgid "Change your password"
msgstr "Đổi mật khẩu"
-#: templates/user/edit-profile.html:174
+#: templates/user/edit-profile.html:156
msgid "Two Factor Authentication is enabled."
msgstr "Two Factor Authentication đã được kích hoạt."
-#: templates/user/edit-profile.html:181
+#: templates/user/edit-profile.html:163
msgid "Two Factor Authentication is disabled."
msgstr "Two Factor Authentication đã được hủy kích hoạt."
-#: templates/user/edit-profile.html:186
+#: templates/user/edit-profile.html:168
msgid "User-script"
msgstr ""
-#: templates/user/edit-profile.html:190
+#: templates/user/edit-profile.html:172
msgid "Update profile"
msgstr "Cập nhật thông tin"
@@ -5818,6 +5749,18 @@ msgstr "Thông tin"
msgid "Check all"
msgstr "Chọn tất cả"
+#~ msgid "Subscribe to contest updates"
+#~ msgstr "Đăng ký để nhận thông báo về các kỳ thi"
+
+#~ msgid "Enable experimental features"
+#~ msgstr "Sử dụng các tính năng thử nghiệm"
+
+#~ msgid "Subscribe to newsletter?"
+#~ msgstr "Đăng ký để nhận thông báo?"
+
+#~ msgid "Notify me about upcoming contests"
+#~ msgstr "Nhận thông báo về các kỳ thi tương lai"
+
#~ msgid "Frozen"
#~ msgstr "Đã đóng băng"
diff --git a/templates/contest/contest-tabs.html b/templates/contest/contest-tabs.html
index f6094e8..3ef2141 100644
--- a/templates/contest/contest-tabs.html
+++ b/templates/contest/contest-tabs.html
@@ -24,6 +24,7 @@
{% endif %}
{% if request.user.is_superuser and can_use_resolver %}
{{ make_tab_item('resolver', 'fa fa-check', url('resolver', contest.key), _('Resolver')) }}
+ {{ make_tab_item('final_ranking', 'fa fa-bar-chart', url('contest_final_ranking', contest.key), _('Final rankings')) }}
{% endif %}
{% if can_edit %}
{% if perms.judge.moss_contest and has_moss_api_key %}
diff --git a/templates/contest/media-js.html b/templates/contest/media-js.html
index 9d12dec..d11b9ef 100644
--- a/templates/contest/media-js.html
+++ b/templates/contest/media-js.html
@@ -118,8 +118,12 @@
var friend = $('#show-friends-checkbox').is(':checked');
var virtual = $('#show-virtual-checkbox').is(':checked');
$('#loading-gif').show();
+ var url = `{{url('contest_ranking_ajax', contest.key)}}?friend=${friend}&virtual=${virtual}`;
+ {% if page_type == 'final_ranking' %}
+ url += "&final=true";
+ {% endif %}
$.get({
- url: `{{url('contest_ranking_ajax', contest.key)}}?friend=${friend}&virtual=${virtual}`,
+ url: url,
success: function(HTML) {
$('#users-table').html(HTML);
highlightFirstSolve();
@@ -199,7 +203,8 @@
$('#show-virtual-label').hide();
{% else %}
{% if request.in_contest %}
- setInterval(update_ranking, 60 * 1000);
+ clearInterval(window.rankingInterval);
+ window.rankingInterval = setInterval(update_ranking, 60 * 1000);
{% endif %}
{% endif %}
});
diff --git a/templates/contest/ranking.html b/templates/contest/ranking.html
index ed04b40..3bd8176 100644
--- a/templates/contest/ranking.html
+++ b/templates/contest/ranking.html
@@ -57,7 +57,7 @@
});
{% endif %}
- {% if page_type == 'ranking' %}
+ {% if page_type == 'ranking' or page_type == 'final_ranking' %}