diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fc3bd4b..8740838 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,3 +11,7 @@ repos: rev: 22.12.0 hooks: - id: black + - repo: https://github.com/hadialqattan/pycln + rev: 'v2.3.0' + hooks: + - id: pycln diff --git a/chat_box/migrations/0015_room_last_msg_time.py b/chat_box/migrations/0015_room_last_msg_time.py new file mode 100644 index 0000000..a32f21b --- /dev/null +++ b/chat_box/migrations/0015_room_last_msg_time.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.18 on 2023-11-02 01:41 + +from django.db import migrations, models + + +def migrate(apps, schema_editor): + Room = apps.get_model("chat_box", "Room") + Message = apps.get_model("chat_box", "Message") + + for room in Room.objects.all(): + messages = room.message_set + last_msg = messages.first() + if last_msg: + room.last_msg_time = last_msg.time + room.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("chat_box", "0014_userroom_unread_count"), + ] + + operations = [ + migrations.AddField( + model_name="room", + name="last_msg_time", + field=models.DateTimeField( + db_index=True, null=True, verbose_name="last seen" + ), + ), + migrations.RunPython(migrate, migrations.RunPython.noop, atomic=True), + ] diff --git a/chat_box/models.py b/chat_box/models.py index fb6de76..fd35430 100644 --- a/chat_box/models.py +++ b/chat_box/models.py @@ -1,6 +1,7 @@ from django.db import models from django.db.models import CASCADE, Q from django.utils.translation import gettext_lazy as _ +from django.utils.functional import cached_property from judge.models.profile import Profile @@ -17,25 +18,40 @@ class Room(models.Model): user_two = models.ForeignKey( Profile, related_name="user_two", verbose_name="user 2", on_delete=CASCADE ) + last_msg_time = models.DateTimeField( + verbose_name=_("last seen"), null=True, db_index=True + ) class Meta: app_label = "chat_box" - @cache_wrapper(prefix="Rc") - def contain(self, profile): - return self.user_one == profile or self.user_two == profile + @cache_wrapper(prefix="Rinfo") + def _info(self): + last_msg = self.message_set.first() + return { + "user_ids": [self.user_one.id, self.user_two.id], + "last_message": last_msg.body if last_msg else None, + } + + @cached_property + def _cached_info(self): + return self._info() + + def contain(self, profile): + return profile.id in self._cached_info["user_ids"] - @cache_wrapper(prefix="Rou") def other_user(self, profile): return self.user_one if profile == self.user_two else self.user_two - @cache_wrapper(prefix="Rus") + def other_user_id(self, profile): + user_ids = self._cached_info["user_ids"] + return sum(user_ids) - profile.id + def users(self): return [self.user_one, self.user_two] - @cache_wrapper(prefix="Rlmb") def last_message_body(self): - return self.message_set.first().body + return self._cached_info["last_message"] class Message(models.Model): diff --git a/chat_box/views.py b/chat_box/views.py index 48fa37a..b01994a 100644 --- a/chat_box/views.py +++ b/chat_box/views.py @@ -29,7 +29,6 @@ from django.utils import timezone from django.contrib.auth.decorators import login_required from django.urls import reverse -import datetime from judge import event_poster as event from judge.jinja2.gravatar import gravatar @@ -38,8 +37,6 @@ from judge.models import Friend from chat_box.models import Message, Profile, Room, UserRoom, Ignore from chat_box.utils import encrypt_url, decrypt_url, encrypt_channel, get_unread_boxes -import json - class ChatView(ListView): context_object_name = "message" @@ -87,8 +84,8 @@ class ChatView(ListView): self.room_id = request_room self.messages = ( Message.objects.filter(hidden=False, room=self.room_id, id__lt=last_id) - .select_related("author", "author__user") - .defer("author__about", "author__user_script")[:page_size] + .select_related("author") + .only("body", "time", "author__rating", "author__display_rank")[:page_size] ) if not only_messages: return super().get(request, *args, **kwargs) @@ -207,7 +204,9 @@ def post_message(request): }, ) else: - Room.last_message_body.dirty(room) + Room._info.dirty(room) + room.last_msg_time = new_message.time + room.save() for user in room.users(): event.post( @@ -354,11 +353,11 @@ def get_online_status(profile, other_profile_ids, rooms=None): room = Room.objects.get(id=i["room"]) other_profile = room.other_user(profile) count[other_profile.id] = i["unread_count"] + rooms = Room.objects.filter(id__in=rooms) for room in rooms: - room = Room.objects.get(id=room) - other_profile = room.other_user(profile) - last_msg[other_profile.id] = room.last_message_body() - room_of_user[other_profile.id] = room.id + other_profile_id = room.other_user_id(profile) + last_msg[other_profile_id] = room.last_message_body() + room_of_user[other_profile_id] = room.id for other_profile in other_profiles: is_online = False @@ -392,9 +391,6 @@ def get_status_context(profile, include_ignored=False): recent_profile = ( Room.objects.filter(Q(user_one=profile) | Q(user_two=profile)) .annotate( - last_msg_time=Subquery( - Message.objects.filter(room=OuterRef("pk")).values("time")[:1] - ), other_user=Case( When(user_one=profile, then="user_two"), default="user_one", @@ -415,28 +411,15 @@ def get_status_context(profile, include_ignored=False): .values_list("id", flat=True) ) - all_user_status = ( - queryset.filter(last_access__gte=last_5_minutes) - .annotate(is_online=Case(default=True, output_field=BooleanField())) - .order_by("-rating") - .exclude(id__in=admin_list) - .exclude(id__in=recent_profile_ids) - .values_list("id", flat=True)[:30] - ) - return [ { - "title": "Recent", + "title": _("Recent"), "user_list": get_online_status(profile, recent_profile_ids, recent_rooms), }, { - "title": "Admin", + "title": _("Admin"), "user_list": get_online_status(profile, admin_list), }, - { - "title": "Other", - "user_list": get_online_status(profile, all_user_status), - }, ] diff --git a/dmoj/settings.py b/dmoj/settings.py index e136a4d..1035969 100644 --- a/dmoj/settings.py +++ b/dmoj/settings.py @@ -9,7 +9,6 @@ https://docs.djangoproject.com/en/1.11/ref/settings/ """ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import datetime import os import tempfile diff --git a/dmoj/urls.py b/dmoj/urls.py index e6610f8..8ac0a29 100644 --- a/dmoj/urls.py +++ b/dmoj/urls.py @@ -1100,6 +1100,11 @@ urlpatterns = [ internal.InternalProblem.as_view(), name="internal_problem", ), + url( + r"^problem_votes$", + internal.get_problem_votes, + name="internal_problem_votes", + ), url( r"^request_time$", internal.InternalRequestTime.as_view(), diff --git a/judge/bridge/judge_handler.py b/judge/bridge/judge_handler.py index 2f13b8a..6aefc5c 100644 --- a/judge/bridge/judge_handler.py +++ b/judge/bridge/judge_handler.py @@ -3,7 +3,6 @@ import json import logging import threading import time -import os from collections import deque, namedtuple from operator import itemgetter @@ -25,6 +24,7 @@ from judge.models import ( Submission, SubmissionTestCase, ) +from judge.bridge.utils import VanishedSubmission logger = logging.getLogger("judge.bridge") json_log = logging.getLogger("judge.json.bridge") @@ -94,12 +94,6 @@ class JudgeHandler(ZlibPacketHandler): def on_disconnect(self): self._stop_ping.set() - if self._working: - logger.error( - "Judge %s disconnected while handling submission %s", - self.name, - self._working, - ) self.judges.remove(self) if self.name is not None: self._disconnected() @@ -119,16 +113,6 @@ class JudgeHandler(ZlibPacketHandler): None, 0, ) - # Submission.objects.filter(id=self._working).update( - # status="IE", result="IE", error="" - # ) - # json_log.error( - # self._make_json_log( - # sub=self._working, - # action="close", - # info="IE due to shutdown on grading", - # ) - # ) def _authenticate(self, id, key): try: @@ -327,6 +311,9 @@ class JudgeHandler(ZlibPacketHandler): def submit(self, id, problem, language, source): data = self.get_related_submission_data(id) + if not data: + self._update_internal_error_submission(id, "Submission vanished") + raise VanishedSubmission() self._working = id self._working_data = { "problem": problem, @@ -675,8 +662,11 @@ class JudgeHandler(ZlibPacketHandler): self._free_self(packet) id = packet["submission-id"] + self._update_internal_error_submission(id, packet["message"]) + + def _update_internal_error_submission(self, id, message): if Submission.objects.filter(id=id).update( - status="IE", result="IE", error=packet["message"] + status="IE", result="IE", error=message ): event.post( "sub_%s" % Submission.get_id_secret(id), {"type": "internal-error"} @@ -684,9 +674,9 @@ class JudgeHandler(ZlibPacketHandler): self._post_update_submission(id, "internal-error", done=True) json_log.info( self._make_json_log( - packet, + sub=id, action="internal-error", - message=packet["message"], + message=message, finish=True, result="IE", ) @@ -695,10 +685,10 @@ class JudgeHandler(ZlibPacketHandler): logger.warning("Unknown submission: %s", id) json_log.error( self._make_json_log( - packet, + sub=id, action="internal-error", info="unknown submission", - message=packet["message"], + message=message, finish=True, result="IE", ) diff --git a/judge/bridge/judge_list.py b/judge/bridge/judge_list.py index 0552de4..bf2b54a 100644 --- a/judge/bridge/judge_list.py +++ b/judge/bridge/judge_list.py @@ -3,6 +3,8 @@ from collections import namedtuple from operator import attrgetter from threading import RLock +from judge.bridge.utils import VanishedSubmission + try: from llist import dllist except ImportError: @@ -39,6 +41,8 @@ class JudgeList(object): ) try: judge.submit(id, problem, language, source) + except VanishedSubmission: + pass except Exception: logger.exception( "Failed to dispatch %d (%s, %s) to %s", diff --git a/judge/bridge/utils.py b/judge/bridge/utils.py new file mode 100644 index 0000000..dfb2ac9 --- /dev/null +++ b/judge/bridge/utils.py @@ -0,0 +1,2 @@ +class VanishedSubmission(Exception): + pass diff --git a/judge/caching.py b/judge/caching.py index 43479da..029cf08 100644 --- a/judge/caching.py +++ b/judge/caching.py @@ -40,7 +40,10 @@ def cache_wrapper(prefix, timeout=None): def _get(key): if not l0_cache: return cache.get(key) - return l0_cache.get(key) or cache.get(key) + result = l0_cache.get(key) + if result is None: + result = cache.get(key) + return result def _set_l0(key, value): if l0_cache: @@ -56,7 +59,7 @@ def cache_wrapper(prefix, timeout=None): result = _get(cache_key) if result is not None: _set_l0(cache_key, result) - if result == NONE_RESULT: + if type(result) == str and result == NONE_RESULT: result = None return result result = func(*args, **kwargs) diff --git a/judge/comments.py b/judge/comments.py index dde97a0..98434cf 100644 --- a/judge/comments.py +++ b/judge/comments.py @@ -144,14 +144,16 @@ class CommentedDetailView(TemplateResponseMixin, SingleObjectMixin, View): def get(self, request, *args, **kwargs): target_comment = None + self.object = self.get_object() if "comment-id" in request.GET: - comment_id = int(request.GET["comment-id"]) try: + comment_id = int(request.GET["comment-id"]) comment_obj = Comment.objects.get(id=comment_id) - except Comment.DoesNotExist: + except (Comment.DoesNotExist, ValueError): + raise Http404 + if comment_obj.linked_object != self.object: raise Http404 target_comment = comment_obj.get_root() - self.object = self.get_object() return self.render_to_response( self.get_context_data( object=self.object, diff --git a/judge/forms.py b/judge/forms.py index e6bbd67..35251ae 100644 --- a/judge/forms.py +++ b/judge/forms.py @@ -11,7 +11,6 @@ from django.contrib.auth.models import User from django.contrib.auth.forms import AuthenticationForm from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.core.validators import RegexValidator -from django.db import transaction from django.db.models import Q from django.forms import ( CharField, @@ -52,7 +51,6 @@ from judge.widgets import ( DateTimePickerWidget, ImageWidget, ) -from judge.tasks import rescore_contest def fix_unicode(string, unsafe=tuple("\u202a\u202b\u202d\u202e")): @@ -282,16 +280,9 @@ class EditOrganizationContestForm(ModelForm): "view_contest_scoreboard", ]: self.fields[field].widget.data_url = ( - self.fields[field].widget.get_url() + "?org_id=1" + self.fields[field].widget.get_url() + f"?org_id={self.org_id}" ) - def save(self, commit=True): - res = super(EditOrganizationContestForm, self).save(commit=False) - if commit: - res.save() - transaction.on_commit(rescore_contest.s(res.key).delay) - return res - class Meta: model = Contest fields = ( diff --git a/judge/logging.py b/judge/logging.py new file mode 100644 index 0000000..8d9f154 --- /dev/null +++ b/judge/logging.py @@ -0,0 +1,7 @@ +import logging + +error_log = logging.getLogger("judge.errors") + + +def log_exception(msg): + error_log.exception(msg) diff --git a/judge/management/commands/generate_data.py b/judge/management/commands/generate_data.py index 83827d9..4fcaa4e 100644 --- a/judge/management/commands/generate_data.py +++ b/judge/management/commands/generate_data.py @@ -1,6 +1,5 @@ from django.core.management.base import BaseCommand from judge.models import * -from collections import defaultdict import csv import os from django.conf import settings diff --git a/judge/migrations/0150_alter_profile_timezone.py b/judge/migrations/0150_alter_profile_timezone.py index 94287b7..20b3e12 100644 --- a/judge/migrations/0150_alter_profile_timezone.py +++ b/judge/migrations/0150_alter_profile_timezone.py @@ -6,13 +6,671 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('judge', '0149_auto_20230202_0902'), + ("judge", "0149_auto_20230202_0902"), ] operations = [ migrations.AlterField( - model_name='profile', - name='timezone', - field=models.CharField(choices=[('Africa', [('Africa/Abidjan', 'Abidjan'), ('Africa/Accra', 'Accra'), ('Africa/Addis_Ababa', 'Addis_Ababa'), ('Africa/Algiers', 'Algiers'), ('Africa/Asmara', 'Asmara'), ('Africa/Asmera', 'Asmera'), ('Africa/Bamako', 'Bamako'), ('Africa/Bangui', 'Bangui'), ('Africa/Banjul', 'Banjul'), ('Africa/Bissau', 'Bissau'), ('Africa/Blantyre', 'Blantyre'), ('Africa/Brazzaville', 'Brazzaville'), ('Africa/Bujumbura', 'Bujumbura'), ('Africa/Cairo', 'Cairo'), ('Africa/Casablanca', 'Casablanca'), ('Africa/Ceuta', 'Ceuta'), ('Africa/Conakry', 'Conakry'), ('Africa/Dakar', 'Dakar'), ('Africa/Dar_es_Salaam', 'Dar_es_Salaam'), ('Africa/Djibouti', 'Djibouti'), ('Africa/Douala', 'Douala'), ('Africa/El_Aaiun', 'El_Aaiun'), ('Africa/Freetown', 'Freetown'), ('Africa/Gaborone', 'Gaborone'), ('Africa/Harare', 'Harare'), ('Africa/Johannesburg', 'Johannesburg'), ('Africa/Juba', 'Juba'), ('Africa/Kampala', 'Kampala'), ('Africa/Khartoum', 'Khartoum'), ('Africa/Kigali', 'Kigali'), ('Africa/Kinshasa', 'Kinshasa'), ('Africa/Lagos', 'Lagos'), ('Africa/Libreville', 'Libreville'), ('Africa/Lome', 'Lome'), ('Africa/Luanda', 'Luanda'), ('Africa/Lubumbashi', 'Lubumbashi'), ('Africa/Lusaka', 'Lusaka'), ('Africa/Malabo', 'Malabo'), ('Africa/Maputo', 'Maputo'), ('Africa/Maseru', 'Maseru'), ('Africa/Mbabane', 'Mbabane'), ('Africa/Mogadishu', 'Mogadishu'), ('Africa/Monrovia', 'Monrovia'), ('Africa/Nairobi', 'Nairobi'), ('Africa/Ndjamena', 'Ndjamena'), ('Africa/Niamey', 'Niamey'), ('Africa/Nouakchott', 'Nouakchott'), ('Africa/Ouagadougou', 'Ouagadougou'), ('Africa/Porto-Novo', 'Porto-Novo'), ('Africa/Sao_Tome', 'Sao_Tome'), ('Africa/Timbuktu', 'Timbuktu'), ('Africa/Tripoli', 'Tripoli'), ('Africa/Tunis', 'Tunis'), ('Africa/Windhoek', 'Windhoek')]), ('America', [('America/Adak', 'Adak'), ('America/Anchorage', 'Anchorage'), ('America/Anguilla', 'Anguilla'), ('America/Antigua', 'Antigua'), ('America/Araguaina', 'Araguaina'), ('America/Argentina/Buenos_Aires', 'Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'Argentina/Cordoba'), ('America/Argentina/Jujuy', 'Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'Argentina/Salta'), ('America/Argentina/San_Juan', 'Argentina/San_Juan'), ('America/Argentina/San_Luis', 'Argentina/San_Luis'), ('America/Argentina/Tucuman', 'Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'Argentina/Ushuaia'), ('America/Aruba', 'Aruba'), ('America/Asuncion', 'Asuncion'), ('America/Atikokan', 'Atikokan'), ('America/Atka', 'Atka'), ('America/Bahia', 'Bahia'), ('America/Bahia_Banderas', 'Bahia_Banderas'), ('America/Barbados', 'Barbados'), ('America/Belem', 'Belem'), ('America/Belize', 'Belize'), ('America/Blanc-Sablon', 'Blanc-Sablon'), ('America/Boa_Vista', 'Boa_Vista'), ('America/Bogota', 'Bogota'), ('America/Boise', 'Boise'), ('America/Buenos_Aires', 'Buenos_Aires'), ('America/Cambridge_Bay', 'Cambridge_Bay'), ('America/Campo_Grande', 'Campo_Grande'), ('America/Cancun', 'Cancun'), ('America/Caracas', 'Caracas'), ('America/Catamarca', 'Catamarca'), ('America/Cayenne', 'Cayenne'), ('America/Cayman', 'Cayman'), ('America/Chicago', 'Chicago'), ('America/Chihuahua', 'Chihuahua'), ('America/Coral_Harbour', 'Coral_Harbour'), ('America/Cordoba', 'Cordoba'), ('America/Costa_Rica', 'Costa_Rica'), ('America/Creston', 'Creston'), ('America/Cuiaba', 'Cuiaba'), ('America/Curacao', 'Curacao'), ('America/Danmarkshavn', 'Danmarkshavn'), ('America/Dawson', 'Dawson'), ('America/Dawson_Creek', 'Dawson_Creek'), ('America/Denver', 'Denver'), ('America/Detroit', 'Detroit'), ('America/Dominica', 'Dominica'), ('America/Edmonton', 'Edmonton'), ('America/Eirunepe', 'Eirunepe'), ('America/El_Salvador', 'El_Salvador'), ('America/Ensenada', 'Ensenada'), ('America/Fort_Nelson', 'Fort_Nelson'), ('America/Fort_Wayne', 'Fort_Wayne'), ('America/Fortaleza', 'Fortaleza'), ('America/Glace_Bay', 'Glace_Bay'), ('America/Godthab', 'Godthab'), ('America/Goose_Bay', 'Goose_Bay'), ('America/Grand_Turk', 'Grand_Turk'), ('America/Grenada', 'Grenada'), ('America/Guadeloupe', 'Guadeloupe'), ('America/Guatemala', 'Guatemala'), ('America/Guayaquil', 'Guayaquil'), ('America/Guyana', 'Guyana'), ('America/Halifax', 'Halifax'), ('America/Havana', 'Havana'), ('America/Hermosillo', 'Hermosillo'), ('America/Indiana/Indianapolis', 'Indiana/Indianapolis'), ('America/Indiana/Knox', 'Indiana/Knox'), ('America/Indiana/Marengo', 'Indiana/Marengo'), ('America/Indiana/Petersburg', 'Indiana/Petersburg'), ('America/Indiana/Tell_City', 'Indiana/Tell_City'), ('America/Indiana/Vevay', 'Indiana/Vevay'), ('America/Indiana/Vincennes', 'Indiana/Vincennes'), ('America/Indiana/Winamac', 'Indiana/Winamac'), ('America/Indianapolis', 'Indianapolis'), ('America/Inuvik', 'Inuvik'), ('America/Iqaluit', 'Iqaluit'), ('America/Jamaica', 'Jamaica'), ('America/Jujuy', 'Jujuy'), ('America/Juneau', 'Juneau'), ('America/Kentucky/Louisville', 'Kentucky/Louisville'), ('America/Kentucky/Monticello', 'Kentucky/Monticello'), ('America/Knox_IN', 'Knox_IN'), ('America/Kralendijk', 'Kralendijk'), ('America/La_Paz', 'La_Paz'), ('America/Lima', 'Lima'), ('America/Los_Angeles', 'Los_Angeles'), ('America/Louisville', 'Louisville'), ('America/Lower_Princes', 'Lower_Princes'), ('America/Maceio', 'Maceio'), ('America/Managua', 'Managua'), ('America/Manaus', 'Manaus'), ('America/Marigot', 'Marigot'), ('America/Martinique', 'Martinique'), ('America/Matamoros', 'Matamoros'), ('America/Mazatlan', 'Mazatlan'), ('America/Mendoza', 'Mendoza'), ('America/Menominee', 'Menominee'), ('America/Merida', 'Merida'), ('America/Metlakatla', 'Metlakatla'), ('America/Mexico_City', 'Mexico_City'), ('America/Miquelon', 'Miquelon'), ('America/Moncton', 'Moncton'), ('America/Monterrey', 'Monterrey'), ('America/Montevideo', 'Montevideo'), ('America/Montreal', 'Montreal'), ('America/Montserrat', 'Montserrat'), ('America/Nassau', 'Nassau'), ('America/New_York', 'New_York'), ('America/Nipigon', 'Nipigon'), ('America/Nome', 'Nome'), ('America/Noronha', 'Noronha'), ('America/North_Dakota/Beulah', 'North_Dakota/Beulah'), ('America/North_Dakota/Center', 'North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'North_Dakota/New_Salem'), ('America/Nuuk', 'Nuuk'), ('America/Ojinaga', 'Ojinaga'), ('America/Panama', 'Panama'), ('America/Pangnirtung', 'Pangnirtung'), ('America/Paramaribo', 'Paramaribo'), ('America/Phoenix', 'Phoenix'), ('America/Port-au-Prince', 'Port-au-Prince'), ('America/Port_of_Spain', 'Port_of_Spain'), ('America/Porto_Acre', 'Porto_Acre'), ('America/Porto_Velho', 'Porto_Velho'), ('America/Puerto_Rico', 'Puerto_Rico'), ('America/Punta_Arenas', 'Punta_Arenas'), ('America/Rainy_River', 'Rainy_River'), ('America/Rankin_Inlet', 'Rankin_Inlet'), ('America/Recife', 'Recife'), ('America/Regina', 'Regina'), ('America/Resolute', 'Resolute'), ('America/Rio_Branco', 'Rio_Branco'), ('America/Rosario', 'Rosario'), ('America/Santa_Isabel', 'Santa_Isabel'), ('America/Santarem', 'Santarem'), ('America/Santiago', 'Santiago'), ('America/Santo_Domingo', 'Santo_Domingo'), ('America/Sao_Paulo', 'Sao_Paulo'), ('America/Scoresbysund', 'Scoresbysund'), ('America/Shiprock', 'Shiprock'), ('America/Sitka', 'Sitka'), ('America/St_Barthelemy', 'St_Barthelemy'), ('America/St_Johns', 'St_Johns'), ('America/St_Kitts', 'St_Kitts'), ('America/St_Lucia', 'St_Lucia'), ('America/St_Thomas', 'St_Thomas'), ('America/St_Vincent', 'St_Vincent'), ('America/Swift_Current', 'Swift_Current'), ('America/Tegucigalpa', 'Tegucigalpa'), ('America/Thule', 'Thule'), ('America/Thunder_Bay', 'Thunder_Bay'), ('America/Tijuana', 'Tijuana'), ('America/Toronto', 'Toronto'), ('America/Tortola', 'Tortola'), ('America/Vancouver', 'Vancouver'), ('America/Virgin', 'Virgin'), ('America/Whitehorse', 'Whitehorse'), ('America/Winnipeg', 'Winnipeg'), ('America/Yakutat', 'Yakutat'), ('America/Yellowknife', 'Yellowknife')]), ('Antarctica', [('Antarctica/Casey', 'Casey'), ('Antarctica/Davis', 'Davis'), ('Antarctica/DumontDUrville', 'DumontDUrville'), ('Antarctica/Macquarie', 'Macquarie'), ('Antarctica/Mawson', 'Mawson'), ('Antarctica/McMurdo', 'McMurdo'), ('Antarctica/Palmer', 'Palmer'), ('Antarctica/Rothera', 'Rothera'), ('Antarctica/South_Pole', 'South_Pole'), ('Antarctica/Syowa', 'Syowa'), ('Antarctica/Troll', 'Troll'), ('Antarctica/Vostok', 'Vostok')]), ('Arctic', [('Arctic/Longyearbyen', 'Longyearbyen')]), ('Asia', [('Asia/Aden', 'Aden'), ('Asia/Almaty', 'Almaty'), ('Asia/Amman', 'Amman'), ('Asia/Anadyr', 'Anadyr'), ('Asia/Aqtau', 'Aqtau'), ('Asia/Aqtobe', 'Aqtobe'), ('Asia/Ashgabat', 'Ashgabat'), ('Asia/Ashkhabad', 'Ashkhabad'), ('Asia/Atyrau', 'Atyrau'), ('Asia/Baghdad', 'Baghdad'), ('Asia/Bahrain', 'Bahrain'), ('Asia/Baku', 'Baku'), ('Asia/Bangkok', 'Bangkok'), ('Asia/Barnaul', 'Barnaul'), ('Asia/Beirut', 'Beirut'), ('Asia/Bishkek', 'Bishkek'), ('Asia/Brunei', 'Brunei'), ('Asia/Calcutta', 'Calcutta'), ('Asia/Chita', 'Chita'), ('Asia/Choibalsan', 'Choibalsan'), ('Asia/Chongqing', 'Chongqing'), ('Asia/Chungking', 'Chungking'), ('Asia/Colombo', 'Colombo'), ('Asia/Dacca', 'Dacca'), ('Asia/Damascus', 'Damascus'), ('Asia/Dhaka', 'Dhaka'), ('Asia/Dili', 'Dili'), ('Asia/Dubai', 'Dubai'), ('Asia/Dushanbe', 'Dushanbe'), ('Asia/Famagusta', 'Famagusta'), ('Asia/Gaza', 'Gaza'), ('Asia/Harbin', 'Harbin'), ('Asia/Hebron', 'Hebron'), ('Asia/Ho_Chi_Minh', 'Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Hong_Kong'), ('Asia/Hovd', 'Hovd'), ('Asia/Irkutsk', 'Irkutsk'), ('Asia/Istanbul', 'Istanbul'), ('Asia/Jakarta', 'Jakarta'), ('Asia/Jayapura', 'Jayapura'), ('Asia/Jerusalem', 'Jerusalem'), ('Asia/Kabul', 'Kabul'), ('Asia/Kamchatka', 'Kamchatka'), ('Asia/Karachi', 'Karachi'), ('Asia/Kashgar', 'Kashgar'), ('Asia/Kathmandu', 'Kathmandu'), ('Asia/Katmandu', 'Katmandu'), ('Asia/Khandyga', 'Khandyga'), ('Asia/Kolkata', 'Kolkata'), ('Asia/Krasnoyarsk', 'Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Kuala_Lumpur'), ('Asia/Kuching', 'Kuching'), ('Asia/Kuwait', 'Kuwait'), ('Asia/Macao', 'Macao'), ('Asia/Macau', 'Macau'), ('Asia/Magadan', 'Magadan'), ('Asia/Makassar', 'Makassar'), ('Asia/Manila', 'Manila'), ('Asia/Muscat', 'Muscat'), ('Asia/Nicosia', 'Nicosia'), ('Asia/Novokuznetsk', 'Novokuznetsk'), ('Asia/Novosibirsk', 'Novosibirsk'), ('Asia/Omsk', 'Omsk'), ('Asia/Oral', 'Oral'), ('Asia/Phnom_Penh', 'Phnom_Penh'), ('Asia/Pontianak', 'Pontianak'), ('Asia/Pyongyang', 'Pyongyang'), ('Asia/Qatar', 'Qatar'), ('Asia/Qostanay', 'Qostanay'), ('Asia/Qyzylorda', 'Qyzylorda'), ('Asia/Rangoon', 'Rangoon'), ('Asia/Riyadh', 'Riyadh'), ('Asia/Saigon', 'Saigon'), ('Asia/Sakhalin', 'Sakhalin'), ('Asia/Samarkand', 'Samarkand'), ('Asia/Seoul', 'Seoul'), ('Asia/Shanghai', 'Shanghai'), ('Asia/Singapore', 'Singapore'), ('Asia/Srednekolymsk', 'Srednekolymsk'), ('Asia/Taipei', 'Taipei'), ('Asia/Tashkent', 'Tashkent'), ('Asia/Tbilisi', 'Tbilisi'), ('Asia/Tehran', 'Tehran'), ('Asia/Tel_Aviv', 'Tel_Aviv'), ('Asia/Thimbu', 'Thimbu'), ('Asia/Thimphu', 'Thimphu'), ('Asia/Tokyo', 'Tokyo'), ('Asia/Tomsk', 'Tomsk'), ('Asia/Ujung_Pandang', 'Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Ulaanbaatar'), ('Asia/Ulan_Bator', 'Ulan_Bator'), ('Asia/Urumqi', 'Urumqi'), ('Asia/Ust-Nera', 'Ust-Nera'), ('Asia/Vientiane', 'Vientiane'), ('Asia/Vladivostok', 'Vladivostok'), ('Asia/Yakutsk', 'Yakutsk'), ('Asia/Yangon', 'Yangon'), ('Asia/Yekaterinburg', 'Yekaterinburg'), ('Asia/Yerevan', 'Yerevan')]), ('Atlantic', [('Atlantic/Azores', 'Azores'), ('Atlantic/Bermuda', 'Bermuda'), ('Atlantic/Canary', 'Canary'), ('Atlantic/Cape_Verde', 'Cape_Verde'), ('Atlantic/Faeroe', 'Faeroe'), ('Atlantic/Faroe', 'Faroe'), ('Atlantic/Jan_Mayen', 'Jan_Mayen'), ('Atlantic/Madeira', 'Madeira'), ('Atlantic/Reykjavik', 'Reykjavik'), ('Atlantic/South_Georgia', 'South_Georgia'), ('Atlantic/St_Helena', 'St_Helena'), ('Atlantic/Stanley', 'Stanley')]), ('Australia', [('Australia/ACT', 'ACT'), ('Australia/Adelaide', 'Adelaide'), ('Australia/Brisbane', 'Brisbane'), ('Australia/Broken_Hill', 'Broken_Hill'), ('Australia/Canberra', 'Canberra'), ('Australia/Currie', 'Currie'), ('Australia/Darwin', 'Darwin'), ('Australia/Eucla', 'Eucla'), ('Australia/Hobart', 'Hobart'), ('Australia/LHI', 'LHI'), ('Australia/Lindeman', 'Lindeman'), ('Australia/Lord_Howe', 'Lord_Howe'), ('Australia/Melbourne', 'Melbourne'), ('Australia/NSW', 'NSW'), ('Australia/North', 'North'), ('Australia/Perth', 'Perth'), ('Australia/Queensland', 'Queensland'), ('Australia/South', 'South'), ('Australia/Sydney', 'Sydney'), ('Australia/Tasmania', 'Tasmania'), ('Australia/Victoria', 'Victoria'), ('Australia/West', 'West'), ('Australia/Yancowinna', 'Yancowinna')]), ('Brazil', [('Brazil/Acre', 'Acre'), ('Brazil/DeNoronha', 'DeNoronha'), ('Brazil/East', 'East'), ('Brazil/West', 'West')]), ('Canada', [('Canada/Atlantic', 'Atlantic'), ('Canada/Central', 'Central'), ('Canada/Eastern', 'Eastern'), ('Canada/Mountain', 'Mountain'), ('Canada/Newfoundland', 'Newfoundland'), ('Canada/Pacific', 'Pacific'), ('Canada/Saskatchewan', 'Saskatchewan'), ('Canada/Yukon', 'Yukon')]), ('Chile', [('Chile/Continental', 'Continental'), ('Chile/EasterIsland', 'EasterIsland')]), ('Etc', [('Etc/Greenwich', 'Greenwich'), ('Etc/UCT', 'UCT'), ('Etc/UTC', 'UTC'), ('Etc/Universal', 'Universal'), ('Etc/Zulu', 'Zulu')]), ('Europe', [('Europe/Amsterdam', 'Amsterdam'), ('Europe/Andorra', 'Andorra'), ('Europe/Astrakhan', 'Astrakhan'), ('Europe/Athens', 'Athens'), ('Europe/Belfast', 'Belfast'), ('Europe/Belgrade', 'Belgrade'), ('Europe/Berlin', 'Berlin'), ('Europe/Bratislava', 'Bratislava'), ('Europe/Brussels', 'Brussels'), ('Europe/Bucharest', 'Bucharest'), ('Europe/Budapest', 'Budapest'), ('Europe/Busingen', 'Busingen'), ('Europe/Chisinau', 'Chisinau'), ('Europe/Copenhagen', 'Copenhagen'), ('Europe/Dublin', 'Dublin'), ('Europe/Gibraltar', 'Gibraltar'), ('Europe/Guernsey', 'Guernsey'), ('Europe/Helsinki', 'Helsinki'), ('Europe/Isle_of_Man', 'Isle_of_Man'), ('Europe/Istanbul', 'Istanbul'), ('Europe/Jersey', 'Jersey'), ('Europe/Kaliningrad', 'Kaliningrad'), ('Europe/Kiev', 'Kiev'), ('Europe/Kirov', 'Kirov'), ('Europe/Lisbon', 'Lisbon'), ('Europe/Ljubljana', 'Ljubljana'), ('Europe/London', 'London'), ('Europe/Luxembourg', 'Luxembourg'), ('Europe/Madrid', 'Madrid'), ('Europe/Malta', 'Malta'), ('Europe/Mariehamn', 'Mariehamn'), ('Europe/Minsk', 'Minsk'), ('Europe/Monaco', 'Monaco'), ('Europe/Moscow', 'Moscow'), ('Europe/Nicosia', 'Nicosia'), ('Europe/Oslo', 'Oslo'), ('Europe/Paris', 'Paris'), ('Europe/Podgorica', 'Podgorica'), ('Europe/Prague', 'Prague'), ('Europe/Riga', 'Riga'), ('Europe/Rome', 'Rome'), ('Europe/Samara', 'Samara'), ('Europe/San_Marino', 'San_Marino'), ('Europe/Sarajevo', 'Sarajevo'), ('Europe/Saratov', 'Saratov'), ('Europe/Simferopol', 'Simferopol'), ('Europe/Skopje', 'Skopje'), ('Europe/Sofia', 'Sofia'), ('Europe/Stockholm', 'Stockholm'), ('Europe/Tallinn', 'Tallinn'), ('Europe/Tirane', 'Tirane'), ('Europe/Tiraspol', 'Tiraspol'), ('Europe/Ulyanovsk', 'Ulyanovsk'), ('Europe/Uzhgorod', 'Uzhgorod'), ('Europe/Vaduz', 'Vaduz'), ('Europe/Vatican', 'Vatican'), ('Europe/Vienna', 'Vienna'), ('Europe/Vilnius', 'Vilnius'), ('Europe/Volgograd', 'Volgograd'), ('Europe/Warsaw', 'Warsaw'), ('Europe/Zagreb', 'Zagreb'), ('Europe/Zaporozhye', 'Zaporozhye'), ('Europe/Zurich', 'Zurich')]), ('Indian', [('Indian/Antananarivo', 'Antananarivo'), ('Indian/Chagos', 'Chagos'), ('Indian/Christmas', 'Christmas'), ('Indian/Cocos', 'Cocos'), ('Indian/Comoro', 'Comoro'), ('Indian/Kerguelen', 'Kerguelen'), ('Indian/Mahe', 'Mahe'), ('Indian/Maldives', 'Maldives'), ('Indian/Mauritius', 'Mauritius'), ('Indian/Mayotte', 'Mayotte'), ('Indian/Reunion', 'Reunion')]), ('Mexico', [('Mexico/BajaNorte', 'BajaNorte'), ('Mexico/BajaSur', 'BajaSur'), ('Mexico/General', 'General')]), ('Other', [('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')]), ('Pacific', [('Pacific/Apia', 'Apia'), ('Pacific/Auckland', 'Auckland'), ('Pacific/Bougainville', 'Bougainville'), ('Pacific/Chatham', 'Chatham'), ('Pacific/Chuuk', 'Chuuk'), ('Pacific/Easter', 'Easter'), ('Pacific/Efate', 'Efate'), ('Pacific/Enderbury', 'Enderbury'), ('Pacific/Fakaofo', 'Fakaofo'), ('Pacific/Fiji', 'Fiji'), ('Pacific/Funafuti', 'Funafuti'), ('Pacific/Galapagos', 'Galapagos'), ('Pacific/Gambier', 'Gambier'), ('Pacific/Guadalcanal', 'Guadalcanal'), ('Pacific/Guam', 'Guam'), ('Pacific/Honolulu', 'Honolulu'), ('Pacific/Johnston', 'Johnston'), ('Pacific/Kanton', 'Kanton'), ('Pacific/Kiritimati', 'Kiritimati'), ('Pacific/Kosrae', 'Kosrae'), ('Pacific/Kwajalein', 'Kwajalein'), ('Pacific/Majuro', 'Majuro'), ('Pacific/Marquesas', 'Marquesas'), ('Pacific/Midway', 'Midway'), ('Pacific/Nauru', 'Nauru'), ('Pacific/Niue', 'Niue'), ('Pacific/Norfolk', 'Norfolk'), ('Pacific/Noumea', 'Noumea'), ('Pacific/Pago_Pago', 'Pago_Pago'), ('Pacific/Palau', 'Palau'), ('Pacific/Pitcairn', 'Pitcairn'), ('Pacific/Pohnpei', 'Pohnpei'), ('Pacific/Ponape', 'Ponape'), ('Pacific/Port_Moresby', 'Port_Moresby'), ('Pacific/Rarotonga', 'Rarotonga'), ('Pacific/Saipan', 'Saipan'), ('Pacific/Samoa', 'Samoa'), ('Pacific/Tahiti', 'Tahiti'), ('Pacific/Tarawa', 'Tarawa'), ('Pacific/Tongatapu', 'Tongatapu'), ('Pacific/Truk', 'Truk'), ('Pacific/Wake', 'Wake'), ('Pacific/Wallis', 'Wallis'), ('Pacific/Yap', 'Yap')]), ('US', [('US/Alaska', 'Alaska'), ('US/Aleutian', 'Aleutian'), ('US/Arizona', 'Arizona'), ('US/Central', 'Central'), ('US/East-Indiana', 'East-Indiana'), ('US/Eastern', 'Eastern'), ('US/Hawaii', 'Hawaii'), ('US/Indiana-Starke', 'Indiana-Starke'), ('US/Michigan', 'Michigan'), ('US/Mountain', 'Mountain'), ('US/Pacific', 'Pacific'), ('US/Samoa', 'Samoa')])], default='Asia/Ho_Chi_Minh', max_length=50, verbose_name='location'), + model_name="profile", + name="timezone", + field=models.CharField( + choices=[ + ( + "Africa", + [ + ("Africa/Abidjan", "Abidjan"), + ("Africa/Accra", "Accra"), + ("Africa/Addis_Ababa", "Addis_Ababa"), + ("Africa/Algiers", "Algiers"), + ("Africa/Asmara", "Asmara"), + ("Africa/Asmera", "Asmera"), + ("Africa/Bamako", "Bamako"), + ("Africa/Bangui", "Bangui"), + ("Africa/Banjul", "Banjul"), + ("Africa/Bissau", "Bissau"), + ("Africa/Blantyre", "Blantyre"), + ("Africa/Brazzaville", "Brazzaville"), + ("Africa/Bujumbura", "Bujumbura"), + ("Africa/Cairo", "Cairo"), + ("Africa/Casablanca", "Casablanca"), + ("Africa/Ceuta", "Ceuta"), + ("Africa/Conakry", "Conakry"), + ("Africa/Dakar", "Dakar"), + ("Africa/Dar_es_Salaam", "Dar_es_Salaam"), + ("Africa/Djibouti", "Djibouti"), + ("Africa/Douala", "Douala"), + ("Africa/El_Aaiun", "El_Aaiun"), + ("Africa/Freetown", "Freetown"), + ("Africa/Gaborone", "Gaborone"), + ("Africa/Harare", "Harare"), + ("Africa/Johannesburg", "Johannesburg"), + ("Africa/Juba", "Juba"), + ("Africa/Kampala", "Kampala"), + ("Africa/Khartoum", "Khartoum"), + ("Africa/Kigali", "Kigali"), + ("Africa/Kinshasa", "Kinshasa"), + ("Africa/Lagos", "Lagos"), + ("Africa/Libreville", "Libreville"), + ("Africa/Lome", "Lome"), + ("Africa/Luanda", "Luanda"), + ("Africa/Lubumbashi", "Lubumbashi"), + ("Africa/Lusaka", "Lusaka"), + ("Africa/Malabo", "Malabo"), + ("Africa/Maputo", "Maputo"), + ("Africa/Maseru", "Maseru"), + ("Africa/Mbabane", "Mbabane"), + ("Africa/Mogadishu", "Mogadishu"), + ("Africa/Monrovia", "Monrovia"), + ("Africa/Nairobi", "Nairobi"), + ("Africa/Ndjamena", "Ndjamena"), + ("Africa/Niamey", "Niamey"), + ("Africa/Nouakchott", "Nouakchott"), + ("Africa/Ouagadougou", "Ouagadougou"), + ("Africa/Porto-Novo", "Porto-Novo"), + ("Africa/Sao_Tome", "Sao_Tome"), + ("Africa/Timbuktu", "Timbuktu"), + ("Africa/Tripoli", "Tripoli"), + ("Africa/Tunis", "Tunis"), + ("Africa/Windhoek", "Windhoek"), + ], + ), + ( + "America", + [ + ("America/Adak", "Adak"), + ("America/Anchorage", "Anchorage"), + ("America/Anguilla", "Anguilla"), + ("America/Antigua", "Antigua"), + ("America/Araguaina", "Araguaina"), + ( + "America/Argentina/Buenos_Aires", + "Argentina/Buenos_Aires", + ), + ("America/Argentina/Catamarca", "Argentina/Catamarca"), + ( + "America/Argentina/ComodRivadavia", + "Argentina/ComodRivadavia", + ), + ("America/Argentina/Cordoba", "Argentina/Cordoba"), + ("America/Argentina/Jujuy", "Argentina/Jujuy"), + ("America/Argentina/La_Rioja", "Argentina/La_Rioja"), + ("America/Argentina/Mendoza", "Argentina/Mendoza"), + ( + "America/Argentina/Rio_Gallegos", + "Argentina/Rio_Gallegos", + ), + ("America/Argentina/Salta", "Argentina/Salta"), + ("America/Argentina/San_Juan", "Argentina/San_Juan"), + ("America/Argentina/San_Luis", "Argentina/San_Luis"), + ("America/Argentina/Tucuman", "Argentina/Tucuman"), + ("America/Argentina/Ushuaia", "Argentina/Ushuaia"), + ("America/Aruba", "Aruba"), + ("America/Asuncion", "Asuncion"), + ("America/Atikokan", "Atikokan"), + ("America/Atka", "Atka"), + ("America/Bahia", "Bahia"), + ("America/Bahia_Banderas", "Bahia_Banderas"), + ("America/Barbados", "Barbados"), + ("America/Belem", "Belem"), + ("America/Belize", "Belize"), + ("America/Blanc-Sablon", "Blanc-Sablon"), + ("America/Boa_Vista", "Boa_Vista"), + ("America/Bogota", "Bogota"), + ("America/Boise", "Boise"), + ("America/Buenos_Aires", "Buenos_Aires"), + ("America/Cambridge_Bay", "Cambridge_Bay"), + ("America/Campo_Grande", "Campo_Grande"), + ("America/Cancun", "Cancun"), + ("America/Caracas", "Caracas"), + ("America/Catamarca", "Catamarca"), + ("America/Cayenne", "Cayenne"), + ("America/Cayman", "Cayman"), + ("America/Chicago", "Chicago"), + ("America/Chihuahua", "Chihuahua"), + ("America/Coral_Harbour", "Coral_Harbour"), + ("America/Cordoba", "Cordoba"), + ("America/Costa_Rica", "Costa_Rica"), + ("America/Creston", "Creston"), + ("America/Cuiaba", "Cuiaba"), + ("America/Curacao", "Curacao"), + ("America/Danmarkshavn", "Danmarkshavn"), + ("America/Dawson", "Dawson"), + ("America/Dawson_Creek", "Dawson_Creek"), + ("America/Denver", "Denver"), + ("America/Detroit", "Detroit"), + ("America/Dominica", "Dominica"), + ("America/Edmonton", "Edmonton"), + ("America/Eirunepe", "Eirunepe"), + ("America/El_Salvador", "El_Salvador"), + ("America/Ensenada", "Ensenada"), + ("America/Fort_Nelson", "Fort_Nelson"), + ("America/Fort_Wayne", "Fort_Wayne"), + ("America/Fortaleza", "Fortaleza"), + ("America/Glace_Bay", "Glace_Bay"), + ("America/Godthab", "Godthab"), + ("America/Goose_Bay", "Goose_Bay"), + ("America/Grand_Turk", "Grand_Turk"), + ("America/Grenada", "Grenada"), + ("America/Guadeloupe", "Guadeloupe"), + ("America/Guatemala", "Guatemala"), + ("America/Guayaquil", "Guayaquil"), + ("America/Guyana", "Guyana"), + ("America/Halifax", "Halifax"), + ("America/Havana", "Havana"), + ("America/Hermosillo", "Hermosillo"), + ("America/Indiana/Indianapolis", "Indiana/Indianapolis"), + ("America/Indiana/Knox", "Indiana/Knox"), + ("America/Indiana/Marengo", "Indiana/Marengo"), + ("America/Indiana/Petersburg", "Indiana/Petersburg"), + ("America/Indiana/Tell_City", "Indiana/Tell_City"), + ("America/Indiana/Vevay", "Indiana/Vevay"), + ("America/Indiana/Vincennes", "Indiana/Vincennes"), + ("America/Indiana/Winamac", "Indiana/Winamac"), + ("America/Indianapolis", "Indianapolis"), + ("America/Inuvik", "Inuvik"), + ("America/Iqaluit", "Iqaluit"), + ("America/Jamaica", "Jamaica"), + ("America/Jujuy", "Jujuy"), + ("America/Juneau", "Juneau"), + ("America/Kentucky/Louisville", "Kentucky/Louisville"), + ("America/Kentucky/Monticello", "Kentucky/Monticello"), + ("America/Knox_IN", "Knox_IN"), + ("America/Kralendijk", "Kralendijk"), + ("America/La_Paz", "La_Paz"), + ("America/Lima", "Lima"), + ("America/Los_Angeles", "Los_Angeles"), + ("America/Louisville", "Louisville"), + ("America/Lower_Princes", "Lower_Princes"), + ("America/Maceio", "Maceio"), + ("America/Managua", "Managua"), + ("America/Manaus", "Manaus"), + ("America/Marigot", "Marigot"), + ("America/Martinique", "Martinique"), + ("America/Matamoros", "Matamoros"), + ("America/Mazatlan", "Mazatlan"), + ("America/Mendoza", "Mendoza"), + ("America/Menominee", "Menominee"), + ("America/Merida", "Merida"), + ("America/Metlakatla", "Metlakatla"), + ("America/Mexico_City", "Mexico_City"), + ("America/Miquelon", "Miquelon"), + ("America/Moncton", "Moncton"), + ("America/Monterrey", "Monterrey"), + ("America/Montevideo", "Montevideo"), + ("America/Montreal", "Montreal"), + ("America/Montserrat", "Montserrat"), + ("America/Nassau", "Nassau"), + ("America/New_York", "New_York"), + ("America/Nipigon", "Nipigon"), + ("America/Nome", "Nome"), + ("America/Noronha", "Noronha"), + ("America/North_Dakota/Beulah", "North_Dakota/Beulah"), + ("America/North_Dakota/Center", "North_Dakota/Center"), + ( + "America/North_Dakota/New_Salem", + "North_Dakota/New_Salem", + ), + ("America/Nuuk", "Nuuk"), + ("America/Ojinaga", "Ojinaga"), + ("America/Panama", "Panama"), + ("America/Pangnirtung", "Pangnirtung"), + ("America/Paramaribo", "Paramaribo"), + ("America/Phoenix", "Phoenix"), + ("America/Port-au-Prince", "Port-au-Prince"), + ("America/Port_of_Spain", "Port_of_Spain"), + ("America/Porto_Acre", "Porto_Acre"), + ("America/Porto_Velho", "Porto_Velho"), + ("America/Puerto_Rico", "Puerto_Rico"), + ("America/Punta_Arenas", "Punta_Arenas"), + ("America/Rainy_River", "Rainy_River"), + ("America/Rankin_Inlet", "Rankin_Inlet"), + ("America/Recife", "Recife"), + ("America/Regina", "Regina"), + ("America/Resolute", "Resolute"), + ("America/Rio_Branco", "Rio_Branco"), + ("America/Rosario", "Rosario"), + ("America/Santa_Isabel", "Santa_Isabel"), + ("America/Santarem", "Santarem"), + ("America/Santiago", "Santiago"), + ("America/Santo_Domingo", "Santo_Domingo"), + ("America/Sao_Paulo", "Sao_Paulo"), + ("America/Scoresbysund", "Scoresbysund"), + ("America/Shiprock", "Shiprock"), + ("America/Sitka", "Sitka"), + ("America/St_Barthelemy", "St_Barthelemy"), + ("America/St_Johns", "St_Johns"), + ("America/St_Kitts", "St_Kitts"), + ("America/St_Lucia", "St_Lucia"), + ("America/St_Thomas", "St_Thomas"), + ("America/St_Vincent", "St_Vincent"), + ("America/Swift_Current", "Swift_Current"), + ("America/Tegucigalpa", "Tegucigalpa"), + ("America/Thule", "Thule"), + ("America/Thunder_Bay", "Thunder_Bay"), + ("America/Tijuana", "Tijuana"), + ("America/Toronto", "Toronto"), + ("America/Tortola", "Tortola"), + ("America/Vancouver", "Vancouver"), + ("America/Virgin", "Virgin"), + ("America/Whitehorse", "Whitehorse"), + ("America/Winnipeg", "Winnipeg"), + ("America/Yakutat", "Yakutat"), + ("America/Yellowknife", "Yellowknife"), + ], + ), + ( + "Antarctica", + [ + ("Antarctica/Casey", "Casey"), + ("Antarctica/Davis", "Davis"), + ("Antarctica/DumontDUrville", "DumontDUrville"), + ("Antarctica/Macquarie", "Macquarie"), + ("Antarctica/Mawson", "Mawson"), + ("Antarctica/McMurdo", "McMurdo"), + ("Antarctica/Palmer", "Palmer"), + ("Antarctica/Rothera", "Rothera"), + ("Antarctica/South_Pole", "South_Pole"), + ("Antarctica/Syowa", "Syowa"), + ("Antarctica/Troll", "Troll"), + ("Antarctica/Vostok", "Vostok"), + ], + ), + ("Arctic", [("Arctic/Longyearbyen", "Longyearbyen")]), + ( + "Asia", + [ + ("Asia/Aden", "Aden"), + ("Asia/Almaty", "Almaty"), + ("Asia/Amman", "Amman"), + ("Asia/Anadyr", "Anadyr"), + ("Asia/Aqtau", "Aqtau"), + ("Asia/Aqtobe", "Aqtobe"), + ("Asia/Ashgabat", "Ashgabat"), + ("Asia/Ashkhabad", "Ashkhabad"), + ("Asia/Atyrau", "Atyrau"), + ("Asia/Baghdad", "Baghdad"), + ("Asia/Bahrain", "Bahrain"), + ("Asia/Baku", "Baku"), + ("Asia/Bangkok", "Bangkok"), + ("Asia/Barnaul", "Barnaul"), + ("Asia/Beirut", "Beirut"), + ("Asia/Bishkek", "Bishkek"), + ("Asia/Brunei", "Brunei"), + ("Asia/Calcutta", "Calcutta"), + ("Asia/Chita", "Chita"), + ("Asia/Choibalsan", "Choibalsan"), + ("Asia/Chongqing", "Chongqing"), + ("Asia/Chungking", "Chungking"), + ("Asia/Colombo", "Colombo"), + ("Asia/Dacca", "Dacca"), + ("Asia/Damascus", "Damascus"), + ("Asia/Dhaka", "Dhaka"), + ("Asia/Dili", "Dili"), + ("Asia/Dubai", "Dubai"), + ("Asia/Dushanbe", "Dushanbe"), + ("Asia/Famagusta", "Famagusta"), + ("Asia/Gaza", "Gaza"), + ("Asia/Harbin", "Harbin"), + ("Asia/Hebron", "Hebron"), + ("Asia/Ho_Chi_Minh", "Ho_Chi_Minh"), + ("Asia/Hong_Kong", "Hong_Kong"), + ("Asia/Hovd", "Hovd"), + ("Asia/Irkutsk", "Irkutsk"), + ("Asia/Istanbul", "Istanbul"), + ("Asia/Jakarta", "Jakarta"), + ("Asia/Jayapura", "Jayapura"), + ("Asia/Jerusalem", "Jerusalem"), + ("Asia/Kabul", "Kabul"), + ("Asia/Kamchatka", "Kamchatka"), + ("Asia/Karachi", "Karachi"), + ("Asia/Kashgar", "Kashgar"), + ("Asia/Kathmandu", "Kathmandu"), + ("Asia/Katmandu", "Katmandu"), + ("Asia/Khandyga", "Khandyga"), + ("Asia/Kolkata", "Kolkata"), + ("Asia/Krasnoyarsk", "Krasnoyarsk"), + ("Asia/Kuala_Lumpur", "Kuala_Lumpur"), + ("Asia/Kuching", "Kuching"), + ("Asia/Kuwait", "Kuwait"), + ("Asia/Macao", "Macao"), + ("Asia/Macau", "Macau"), + ("Asia/Magadan", "Magadan"), + ("Asia/Makassar", "Makassar"), + ("Asia/Manila", "Manila"), + ("Asia/Muscat", "Muscat"), + ("Asia/Nicosia", "Nicosia"), + ("Asia/Novokuznetsk", "Novokuznetsk"), + ("Asia/Novosibirsk", "Novosibirsk"), + ("Asia/Omsk", "Omsk"), + ("Asia/Oral", "Oral"), + ("Asia/Phnom_Penh", "Phnom_Penh"), + ("Asia/Pontianak", "Pontianak"), + ("Asia/Pyongyang", "Pyongyang"), + ("Asia/Qatar", "Qatar"), + ("Asia/Qostanay", "Qostanay"), + ("Asia/Qyzylorda", "Qyzylorda"), + ("Asia/Rangoon", "Rangoon"), + ("Asia/Riyadh", "Riyadh"), + ("Asia/Saigon", "Saigon"), + ("Asia/Sakhalin", "Sakhalin"), + ("Asia/Samarkand", "Samarkand"), + ("Asia/Seoul", "Seoul"), + ("Asia/Shanghai", "Shanghai"), + ("Asia/Singapore", "Singapore"), + ("Asia/Srednekolymsk", "Srednekolymsk"), + ("Asia/Taipei", "Taipei"), + ("Asia/Tashkent", "Tashkent"), + ("Asia/Tbilisi", "Tbilisi"), + ("Asia/Tehran", "Tehran"), + ("Asia/Tel_Aviv", "Tel_Aviv"), + ("Asia/Thimbu", "Thimbu"), + ("Asia/Thimphu", "Thimphu"), + ("Asia/Tokyo", "Tokyo"), + ("Asia/Tomsk", "Tomsk"), + ("Asia/Ujung_Pandang", "Ujung_Pandang"), + ("Asia/Ulaanbaatar", "Ulaanbaatar"), + ("Asia/Ulan_Bator", "Ulan_Bator"), + ("Asia/Urumqi", "Urumqi"), + ("Asia/Ust-Nera", "Ust-Nera"), + ("Asia/Vientiane", "Vientiane"), + ("Asia/Vladivostok", "Vladivostok"), + ("Asia/Yakutsk", "Yakutsk"), + ("Asia/Yangon", "Yangon"), + ("Asia/Yekaterinburg", "Yekaterinburg"), + ("Asia/Yerevan", "Yerevan"), + ], + ), + ( + "Atlantic", + [ + ("Atlantic/Azores", "Azores"), + ("Atlantic/Bermuda", "Bermuda"), + ("Atlantic/Canary", "Canary"), + ("Atlantic/Cape_Verde", "Cape_Verde"), + ("Atlantic/Faeroe", "Faeroe"), + ("Atlantic/Faroe", "Faroe"), + ("Atlantic/Jan_Mayen", "Jan_Mayen"), + ("Atlantic/Madeira", "Madeira"), + ("Atlantic/Reykjavik", "Reykjavik"), + ("Atlantic/South_Georgia", "South_Georgia"), + ("Atlantic/St_Helena", "St_Helena"), + ("Atlantic/Stanley", "Stanley"), + ], + ), + ( + "Australia", + [ + ("Australia/ACT", "ACT"), + ("Australia/Adelaide", "Adelaide"), + ("Australia/Brisbane", "Brisbane"), + ("Australia/Broken_Hill", "Broken_Hill"), + ("Australia/Canberra", "Canberra"), + ("Australia/Currie", "Currie"), + ("Australia/Darwin", "Darwin"), + ("Australia/Eucla", "Eucla"), + ("Australia/Hobart", "Hobart"), + ("Australia/LHI", "LHI"), + ("Australia/Lindeman", "Lindeman"), + ("Australia/Lord_Howe", "Lord_Howe"), + ("Australia/Melbourne", "Melbourne"), + ("Australia/NSW", "NSW"), + ("Australia/North", "North"), + ("Australia/Perth", "Perth"), + ("Australia/Queensland", "Queensland"), + ("Australia/South", "South"), + ("Australia/Sydney", "Sydney"), + ("Australia/Tasmania", "Tasmania"), + ("Australia/Victoria", "Victoria"), + ("Australia/West", "West"), + ("Australia/Yancowinna", "Yancowinna"), + ], + ), + ( + "Brazil", + [ + ("Brazil/Acre", "Acre"), + ("Brazil/DeNoronha", "DeNoronha"), + ("Brazil/East", "East"), + ("Brazil/West", "West"), + ], + ), + ( + "Canada", + [ + ("Canada/Atlantic", "Atlantic"), + ("Canada/Central", "Central"), + ("Canada/Eastern", "Eastern"), + ("Canada/Mountain", "Mountain"), + ("Canada/Newfoundland", "Newfoundland"), + ("Canada/Pacific", "Pacific"), + ("Canada/Saskatchewan", "Saskatchewan"), + ("Canada/Yukon", "Yukon"), + ], + ), + ( + "Chile", + [ + ("Chile/Continental", "Continental"), + ("Chile/EasterIsland", "EasterIsland"), + ], + ), + ( + "Etc", + [ + ("Etc/Greenwich", "Greenwich"), + ("Etc/UCT", "UCT"), + ("Etc/UTC", "UTC"), + ("Etc/Universal", "Universal"), + ("Etc/Zulu", "Zulu"), + ], + ), + ( + "Europe", + [ + ("Europe/Amsterdam", "Amsterdam"), + ("Europe/Andorra", "Andorra"), + ("Europe/Astrakhan", "Astrakhan"), + ("Europe/Athens", "Athens"), + ("Europe/Belfast", "Belfast"), + ("Europe/Belgrade", "Belgrade"), + ("Europe/Berlin", "Berlin"), + ("Europe/Bratislava", "Bratislava"), + ("Europe/Brussels", "Brussels"), + ("Europe/Bucharest", "Bucharest"), + ("Europe/Budapest", "Budapest"), + ("Europe/Busingen", "Busingen"), + ("Europe/Chisinau", "Chisinau"), + ("Europe/Copenhagen", "Copenhagen"), + ("Europe/Dublin", "Dublin"), + ("Europe/Gibraltar", "Gibraltar"), + ("Europe/Guernsey", "Guernsey"), + ("Europe/Helsinki", "Helsinki"), + ("Europe/Isle_of_Man", "Isle_of_Man"), + ("Europe/Istanbul", "Istanbul"), + ("Europe/Jersey", "Jersey"), + ("Europe/Kaliningrad", "Kaliningrad"), + ("Europe/Kiev", "Kiev"), + ("Europe/Kirov", "Kirov"), + ("Europe/Lisbon", "Lisbon"), + ("Europe/Ljubljana", "Ljubljana"), + ("Europe/London", "London"), + ("Europe/Luxembourg", "Luxembourg"), + ("Europe/Madrid", "Madrid"), + ("Europe/Malta", "Malta"), + ("Europe/Mariehamn", "Mariehamn"), + ("Europe/Minsk", "Minsk"), + ("Europe/Monaco", "Monaco"), + ("Europe/Moscow", "Moscow"), + ("Europe/Nicosia", "Nicosia"), + ("Europe/Oslo", "Oslo"), + ("Europe/Paris", "Paris"), + ("Europe/Podgorica", "Podgorica"), + ("Europe/Prague", "Prague"), + ("Europe/Riga", "Riga"), + ("Europe/Rome", "Rome"), + ("Europe/Samara", "Samara"), + ("Europe/San_Marino", "San_Marino"), + ("Europe/Sarajevo", "Sarajevo"), + ("Europe/Saratov", "Saratov"), + ("Europe/Simferopol", "Simferopol"), + ("Europe/Skopje", "Skopje"), + ("Europe/Sofia", "Sofia"), + ("Europe/Stockholm", "Stockholm"), + ("Europe/Tallinn", "Tallinn"), + ("Europe/Tirane", "Tirane"), + ("Europe/Tiraspol", "Tiraspol"), + ("Europe/Ulyanovsk", "Ulyanovsk"), + ("Europe/Uzhgorod", "Uzhgorod"), + ("Europe/Vaduz", "Vaduz"), + ("Europe/Vatican", "Vatican"), + ("Europe/Vienna", "Vienna"), + ("Europe/Vilnius", "Vilnius"), + ("Europe/Volgograd", "Volgograd"), + ("Europe/Warsaw", "Warsaw"), + ("Europe/Zagreb", "Zagreb"), + ("Europe/Zaporozhye", "Zaporozhye"), + ("Europe/Zurich", "Zurich"), + ], + ), + ( + "Indian", + [ + ("Indian/Antananarivo", "Antananarivo"), + ("Indian/Chagos", "Chagos"), + ("Indian/Christmas", "Christmas"), + ("Indian/Cocos", "Cocos"), + ("Indian/Comoro", "Comoro"), + ("Indian/Kerguelen", "Kerguelen"), + ("Indian/Mahe", "Mahe"), + ("Indian/Maldives", "Maldives"), + ("Indian/Mauritius", "Mauritius"), + ("Indian/Mayotte", "Mayotte"), + ("Indian/Reunion", "Reunion"), + ], + ), + ( + "Mexico", + [ + ("Mexico/BajaNorte", "BajaNorte"), + ("Mexico/BajaSur", "BajaSur"), + ("Mexico/General", "General"), + ], + ), + ( + "Other", + [ + ("CET", "CET"), + ("CST6CDT", "CST6CDT"), + ("Cuba", "Cuba"), + ("EET", "EET"), + ("EST", "EST"), + ("EST5EDT", "EST5EDT"), + ("Egypt", "Egypt"), + ("Eire", "Eire"), + ("GB", "GB"), + ("GB-Eire", "GB-Eire"), + ("Greenwich", "Greenwich"), + ("HST", "HST"), + ("Hongkong", "Hongkong"), + ("Iceland", "Iceland"), + ("Iran", "Iran"), + ("Israel", "Israel"), + ("Jamaica", "Jamaica"), + ("Japan", "Japan"), + ("Kwajalein", "Kwajalein"), + ("Libya", "Libya"), + ("MET", "MET"), + ("MST", "MST"), + ("MST7MDT", "MST7MDT"), + ("NZ", "NZ"), + ("NZ-CHAT", "NZ-CHAT"), + ("Navajo", "Navajo"), + ("PRC", "PRC"), + ("PST8PDT", "PST8PDT"), + ("Poland", "Poland"), + ("Portugal", "Portugal"), + ("ROC", "ROC"), + ("ROK", "ROK"), + ("Singapore", "Singapore"), + ("Turkey", "Turkey"), + ("UCT", "UCT"), + ("UTC", "UTC"), + ("Universal", "Universal"), + ("W-SU", "W-SU"), + ("WET", "WET"), + ("Zulu", "Zulu"), + ], + ), + ( + "Pacific", + [ + ("Pacific/Apia", "Apia"), + ("Pacific/Auckland", "Auckland"), + ("Pacific/Bougainville", "Bougainville"), + ("Pacific/Chatham", "Chatham"), + ("Pacific/Chuuk", "Chuuk"), + ("Pacific/Easter", "Easter"), + ("Pacific/Efate", "Efate"), + ("Pacific/Enderbury", "Enderbury"), + ("Pacific/Fakaofo", "Fakaofo"), + ("Pacific/Fiji", "Fiji"), + ("Pacific/Funafuti", "Funafuti"), + ("Pacific/Galapagos", "Galapagos"), + ("Pacific/Gambier", "Gambier"), + ("Pacific/Guadalcanal", "Guadalcanal"), + ("Pacific/Guam", "Guam"), + ("Pacific/Honolulu", "Honolulu"), + ("Pacific/Johnston", "Johnston"), + ("Pacific/Kanton", "Kanton"), + ("Pacific/Kiritimati", "Kiritimati"), + ("Pacific/Kosrae", "Kosrae"), + ("Pacific/Kwajalein", "Kwajalein"), + ("Pacific/Majuro", "Majuro"), + ("Pacific/Marquesas", "Marquesas"), + ("Pacific/Midway", "Midway"), + ("Pacific/Nauru", "Nauru"), + ("Pacific/Niue", "Niue"), + ("Pacific/Norfolk", "Norfolk"), + ("Pacific/Noumea", "Noumea"), + ("Pacific/Pago_Pago", "Pago_Pago"), + ("Pacific/Palau", "Palau"), + ("Pacific/Pitcairn", "Pitcairn"), + ("Pacific/Pohnpei", "Pohnpei"), + ("Pacific/Ponape", "Ponape"), + ("Pacific/Port_Moresby", "Port_Moresby"), + ("Pacific/Rarotonga", "Rarotonga"), + ("Pacific/Saipan", "Saipan"), + ("Pacific/Samoa", "Samoa"), + ("Pacific/Tahiti", "Tahiti"), + ("Pacific/Tarawa", "Tarawa"), + ("Pacific/Tongatapu", "Tongatapu"), + ("Pacific/Truk", "Truk"), + ("Pacific/Wake", "Wake"), + ("Pacific/Wallis", "Wallis"), + ("Pacific/Yap", "Yap"), + ], + ), + ( + "US", + [ + ("US/Alaska", "Alaska"), + ("US/Aleutian", "Aleutian"), + ("US/Arizona", "Arizona"), + ("US/Central", "Central"), + ("US/East-Indiana", "East-Indiana"), + ("US/Eastern", "Eastern"), + ("US/Hawaii", "Hawaii"), + ("US/Indiana-Starke", "Indiana-Starke"), + ("US/Michigan", "Michigan"), + ("US/Mountain", "Mountain"), + ("US/Pacific", "Pacific"), + ("US/Samoa", "Samoa"), + ], + ), + ], + default="Asia/Ho_Chi_Minh", + max_length=50, + verbose_name="location", + ), ), ] diff --git a/judge/ml/collab_filter.py b/judge/ml/collab_filter.py index 1b0a1db..d19c5e5 100644 --- a/judge/ml/collab_filter.py +++ b/judge/ml/collab_filter.py @@ -1,9 +1,10 @@ import numpy as np -from django.conf import settings import os -from django.core.cache import cache import hashlib +from django.core.cache import cache +from django.conf import settings + from judge.caching import cache_wrapper @@ -13,14 +14,13 @@ class CollabFilter: # name = 'collab_filter' or 'collab_filter_time' def __init__(self, name): - embeddings = np.load( + self.embeddings = np.load( os.path.join(settings.ML_OUTPUT_PATH, name + "/embeddings.npz"), allow_pickle=True, ) - arr0, arr1 = embeddings.files + _, problem_arr = self.embeddings.files self.name = name - self.user_embeddings = embeddings[arr0] - self.problem_embeddings = embeddings[arr1] + self.problem_embeddings = self.embeddings[problem_arr] def __str__(self): return self.name @@ -44,18 +44,32 @@ class CollabFilter: scores = u.dot(V.T) return scores + def _get_embedding_version(self): + first_problem = self.problem_embeddings[0] + array_bytes = first_problem.tobytes() + hash_object = hashlib.sha256(array_bytes) + hash_bytes = hash_object.digest() + return hash_bytes.hex()[:5] + + @cache_wrapper(prefix="CFgue", timeout=86400) + def _get_user_embedding(self, user_id, embedding_version): + user_arr, _ = self.embeddings.files + user_embeddings = self.embeddings[user_arr] + if user_id >= len(user_embeddings): + return user_embeddings[0] + return user_embeddings[user_id] + + def get_user_embedding(self, user_id): + version = self._get_embedding_version() + return self._get_user_embedding(user_id, version) + @cache_wrapper(prefix="user_recommendations", timeout=3600) - def user_recommendations(self, user, problems, measure=DOT, limit=None): - uid = user.id - if uid >= len(self.user_embeddings): - uid = 0 - scores = self.compute_scores( - self.user_embeddings[uid], self.problem_embeddings, measure - ) + def user_recommendations(self, user_id, problems, measure=DOT, limit=None): + user_embedding = self.get_user_embedding(user_id) + scores = self.compute_scores(user_embedding, self.problem_embeddings, measure) res = [] # [(score, problem)] for pid in problems: - # pid = problem.id if pid < len(scores): res.append((scores[pid], pid)) diff --git a/judge/models/contest.py b/judge/models/contest.py index bda262d..1432d94 100644 --- a/judge/models/contest.py +++ b/judge/models/contest.py @@ -99,11 +99,13 @@ class Contest(models.Model, PageVotable, Bookmarkable): ) authors = models.ManyToManyField( Profile, + verbose_name=_("authors"), help_text=_("These users will be able to edit the contest."), related_name="authors+", ) curators = models.ManyToManyField( Profile, + verbose_name=_("curators"), help_text=_( "These users will be able to edit the contest, " "but will not be listed as authors." @@ -113,6 +115,7 @@ class Contest(models.Model, PageVotable, Bookmarkable): ) testers = models.ManyToManyField( Profile, + verbose_name=_("testers"), help_text=_( "These users will be able to view the contest, " "but not edit it." ), diff --git a/judge/models/problem.py b/judge/models/problem.py index 47741e9..98e477b 100644 --- a/judge/models/problem.py +++ b/judge/models/problem.py @@ -1,6 +1,5 @@ import errno from operator import attrgetter -from math import sqrt from django.conf import settings from django.contrib.contenttypes.fields import GenericRelation @@ -557,7 +556,7 @@ class Problem(models.Model, PageVotable, Bookmarkable): def save(self, *args, **kwargs): super(Problem, self).save(*args, **kwargs) - if self.code != self.__original_code: + if self.__original_code and self.code != self.__original_code: if hasattr(self, "data_files") or self.pdf_description: try: problem_data_storage.rename(self.__original_code, self.code) diff --git a/judge/models/problem_data.py b/judge/models/problem_data.py index d61b528..b04d2ab 100644 --- a/judge/models/problem_data.py +++ b/judge/models/problem_data.py @@ -162,10 +162,10 @@ class ProblemData(models.Model): get_file_cachekey(file), ) cache.delete(cache_key) - except BadZipFile: + except (BadZipFile, FileNotFoundError): pass - if self.zipfile != self.__original_zipfile and self.__original_zipfile: - self.__original_zipfile.delete(save=False) + if self.zipfile != self.__original_zipfile: + self.__original_zipfile.delete(save=False) return super(ProblemData, self).save(*args, **kwargs) def has_yml(self): diff --git a/judge/tasks/import_users.py b/judge/tasks/import_users.py index 5584687..07acd4e 100644 --- a/judge/tasks/import_users.py +++ b/judge/tasks/import_users.py @@ -1,5 +1,4 @@ import csv -from tempfile import mktemp import re from django.conf import settings diff --git a/judge/utils/fine_uploader.py b/judge/utils/fine_uploader.py index 2a22ccf..8f64d78 100644 --- a/judge/utils/fine_uploader.py +++ b/judge/utils/fine_uploader.py @@ -5,7 +5,6 @@ from django import forms from django.forms import ClearableFileInput import os, os.path -import tempfile import shutil __all__ = ("handle_upload", "save_upload", "FineUploadForm", "FineUploadFileInput") diff --git a/judge/utils/problem_data.py b/judge/utils/problem_data.py index eb56229..023c0be 100644 --- a/judge/utils/problem_data.py +++ b/judge/utils/problem_data.py @@ -2,7 +2,6 @@ import hashlib import json import os import re -import shutil import yaml import zipfile @@ -13,6 +12,8 @@ from django.urls import reverse from django.utils.translation import gettext as _ from django.core.cache import cache +from judge.logging import log_exception + if os.altsep: def split_path_first( @@ -324,11 +325,13 @@ def get_problem_case(problem, files): settings.DMOJ_PROBLEM_DATA_ROOT, str(problem.data_files.zipfile) ) if not os.path.exists(archive_path): - raise Exception('archive file "%s" does not exist' % archive_path) + log_exception('archive file "%s" does not exist' % archive_path) + return {} try: archive = zipfile.ZipFile(archive_path, "r") except zipfile.BadZipfile: - raise Exception('bad archive: "%s"' % archive_path) + log_exception('bad archive: "%s"' % archive_path) + return {} for file in uncached_files: cache_key = "problem_archive:%s:%s" % (problem.code, get_file_cachekey(file)) diff --git a/judge/utils/problems.py b/judge/utils/problems.py index 67ef163..0b69267 100644 --- a/judge/utils/problems.py +++ b/judge/utils/problems.py @@ -1,8 +1,8 @@ from collections import defaultdict from math import e -import os, zipfile -from datetime import datetime +from datetime import datetime, timedelta import random +from enum import Enum from django.conf import settings from django.core.cache import cache @@ -10,6 +10,7 @@ from django.db.models import Case, Count, ExpressionWrapper, F, Max, Q, When from django.db.models.fields import FloatField from django.utils import timezone from django.utils.translation import gettext as _, gettext_noop +from django.http import Http404 from judge.models import Problem, Submission from judge.ml.collab_filter import CollabFilter @@ -249,3 +250,72 @@ def finished_submission(sub): keys += ["contest_complete:%d" % participation.id] keys += ["contest_attempted:%d" % participation.id] cache.delete_many(keys) + + +class RecommendationType(Enum): + HOT_PROBLEM = 1 + CF_DOT = 2 + CF_COSINE = 3 + CF_TIME_DOT = 4 + CF_TIME_COSINE = 5 + + +# Return a list of list. Each inner list correspond to each type in types +def get_user_recommended_problems( + user_id, + problem_ids, + recommendation_types, + limits, + shuffle=False, +): + cf_model = CollabFilter("collab_filter") + cf_time_model = CollabFilter("collab_filter_time") + + def get_problem_ids_from_type(rec_type, limit): + if type(rec_type) == int: + try: + rec_type = RecommendationType(rec_type) + except ValueError: + raise Http404() + if rec_type == RecommendationType.HOT_PROBLEM: + return [ + problem.id + for problem in hot_problems(timedelta(days=7), limit) + if problem.id in set(problem_ids) + ] + if rec_type == RecommendationType.CF_DOT: + return cf_model.user_recommendations( + user_id, problem_ids, cf_model.DOT, limit + ) + if rec_type == RecommendationType.CF_COSINE: + return cf_model.user_recommendations( + user_id, problem_ids, cf_model.COSINE, limit + ) + if rec_type == RecommendationType.CF_TIME_DOT: + return cf_time_model.user_recommendations( + user_id, problem_ids, cf_model.DOT, limit + ) + if rec_type == RecommendationType.CF_TIME_COSINE: + return cf_time_model.user_recommendations( + user_id, problem_ids, cf_model.COSINE, limit + ) + return [] + + all_problems = [] + for rec_type, limit in zip(recommendation_types, limits): + all_problems += get_problem_ids_from_type(rec_type, limit) + if shuffle: + seed = datetime.now().strftime("%d%m%Y") + random.Random(seed).shuffle(all_problems) + + # deduplicate problems + res = [] + used_pid = set() + + for obj in all_problems: + if type(obj) == tuple: + obj = obj[1] + if obj not in used_pid: + res.append(obj) + used_pid.add(obj) + return res diff --git a/judge/views/contests.py b/judge/views/contests.py index a84d98b..80e5fb5 100644 --- a/judge/views/contests.py +++ b/judge/views/contests.py @@ -144,7 +144,11 @@ class ContestList( context_object_name = "past_contests" all_sorts = frozenset(("name", "user_count", "start_time")) default_desc = frozenset(("name", "user_count")) - default_sort = "-start_time" + + def get_default_sort_order(self, request): + if request.GET.get("contest") and settings.ENABLE_FTS: + return "-relevance" + return "-start_time" @cached_property def _now(self): @@ -157,7 +161,7 @@ class ContestList( if request.GET.get("show_orgs"): self.show_orgs = 1 - if "orgs" in self.request.GET and self.request.profile: + if self.request.GET.get("orgs") and self.request.profile: try: self.org_query = list(map(int, request.GET.getlist("orgs"))) if not self.request.user.is_superuser: @@ -165,8 +169,10 @@ class ContestList( i for i in self.org_query if i - in self.request.profile.organizations.values_list( - "id", flat=True + in set( + self.request.profile.organizations.values_list( + "id", flat=True + ) ) ] except ValueError: @@ -181,7 +187,7 @@ class ContestList( .prefetch_related("tags", "organizations", "authors", "curators", "testers") ) - if "contest" in self.request.GET: + if self.request.GET.get("contest"): self.contest_query = query = " ".join( self.request.GET.getlist("contest") ).strip() @@ -249,10 +255,7 @@ class ContestList( context["org_query"] = self.org_query context["show_orgs"] = int(self.show_orgs) if self.request.profile: - if self.request.user.is_superuser: - context["organizations"] = Organization.objects.all() - else: - context["organizations"] = self.request.profile.organizations.all() + context["organizations"] = self.request.profile.organizations.all() context["page_type"] = "list" context.update(self.get_sort_context()) context.update(self.get_sort_paginate_context()) @@ -419,7 +422,14 @@ class ContestDetail( return [] res = [] for organization in self.object.organizations.all(): + can_edit = False if self.request.profile.can_edit_organization(organization): + can_edit = True + if self.request.profile in organization and self.object.is_editable_by( + self.request.user + ): + can_edit = True + if can_edit: res.append(organization) return res @@ -441,16 +451,32 @@ class ContestDetail( .add_i18n_name(self.request.LANGUAGE_CODE) ) context["editable_organizations"] = self.get_editable_organizations() + context["is_clonable"] = is_contest_clonable(self.request, self.object) return context -class ContestClone( - ContestMixin, PermissionRequiredMixin, TitleMixin, SingleObjectFormView -): +def is_contest_clonable(request, contest): + if not request.profile: + return False + if not Organization.objects.filter(admins=request.profile).exists(): + return False + if request.user.has_perm("judge.clone_contest"): + return True + if contest.ended: + return True + return False + + +class ContestClone(ContestMixin, TitleMixin, SingleObjectFormView): title = _("Clone Contest") template_name = "contest/clone.html" form_class = ContestCloneForm - permission_required = "judge.clone_contest" + + def get_object(self, queryset=None): + contest = super().get_object(queryset) + if not is_contest_clonable(self.request, contest): + raise Http404() + return contest def get_form_kwargs(self): kwargs = super().get_form_kwargs() @@ -475,6 +501,7 @@ class ContestClone( contest.is_visible = False contest.user_count = 0 contest.key = form.cleaned_data["key"] + contest.is_rated = False contest.save() contest.tags.set(tags) diff --git a/judge/views/internal.py b/judge/views/internal.py index eff02a4..060a2c3 100644 --- a/judge/views/internal.py +++ b/judge/views/internal.py @@ -6,6 +6,7 @@ from django.utils.translation import gettext as _, gettext_lazy from django.db.models import Count, Q from django.http import HttpResponseForbidden from django.urls import reverse +from django.shortcuts import render from judge.utils.diggpaginator import DiggPaginator from judge.models import VolunteerProblemVote, Problem @@ -21,7 +22,7 @@ class InternalView(object): class InternalProblem(InternalView, ListView): model = Problem title = _("Internal problems") - template_name = "internal/problem.html" + template_name = "internal/problem/problem.html" paginate_by = 100 context_object_name = "problems" @@ -63,6 +64,28 @@ class InternalProblem(InternalView, ListView): return context +def get_problem_votes(request): + if not request.user.is_superuser: + return HttpResponseForbidden() + try: + problem = Problem.objects.get(id=request.GET.get("id")) + except: + return HttpResponseForbidden() + votes = ( + problem.volunteer_user_votes.select_related("voter") + .prefetch_related("types") + .order_by("id") + ) + return render( + request, + "internal/problem/votes.html", + { + "problem": problem, + "votes": votes, + }, + ) + + class RequestTimeMixin(object): def get_requests_data(self): logger = logging.getLogger(self.log_name) diff --git a/judge/views/organization.py b/judge/views/organization.py index be80203..eff02d4 100644 --- a/judge/views/organization.py +++ b/judge/views/organization.py @@ -73,6 +73,7 @@ from judge.views.problem import ProblemList from judge.views.contests import ContestList from judge.views.submission import AllSubmissions, SubmissionsListBase from judge.views.feed import FeedView +from judge.tasks import rescore_contest __all__ = [ "OrganizationList", @@ -394,7 +395,7 @@ class OrganizationContestMixin( model = Contest def is_contest_editable(self, request, contest): - return request.profile in contest.authors.all() or self.can_edit_organization( + return contest.is_editable_by(request.user) or self.can_edit_organization( self.organization ) @@ -947,7 +948,7 @@ class EditOrganizationContest( def get_content_title(self): href = reverse("contest_view", args=[self.contest.key]) - return mark_safe(f'Edit {self.contest.key}') + return mark_safe(_("Edit") + f' {self.contest.key}') def get_object(self): return self.contest @@ -960,6 +961,19 @@ class EditOrganizationContest( self.object.organizations.add(self.organization) self.object.is_organization_private = True self.object.save() + + if any( + f in form.changed_data + for f in ( + "start_time", + "end_time", + "time_limit", + "format_config", + "format_name", + "freeze_after", + ) + ): + transaction.on_commit(rescore_contest.s(self.object.key).delay) return res def get_problem_formset(self, post=False): diff --git a/judge/views/problem.py b/judge/views/problem.py index 0bf304b..3b7cd84 100644 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -1,10 +1,8 @@ import logging import os import shutil -from datetime import timedelta, datetime from operator import itemgetter from random import randrange -import random from copy import deepcopy from django.core.cache import cache @@ -77,6 +75,8 @@ from judge.utils.problems import ( user_attempted_ids, user_completed_ids, get_related_problems, + get_user_recommended_problems, + RecommendationType, ) from judge.utils.strings import safe_float_or_none, safe_int_or_none from judge.utils.tickets import own_ticket_filter @@ -834,24 +834,34 @@ class ProblemFeed(ProblemList, FeedView): title = _("Problem feed") feed_type = None - # arr = [[], [], ..] - def merge_recommendation(self, arr): - seed = datetime.now().strftime("%d%m%Y") - merged_array = [] - for a in arr: - merged_array += a - random.Random(seed).shuffle(merged_array) + def get_recommended_problem_ids(self, queryset): + user_id = self.request.profile.id + problem_ids = queryset.values_list("id", flat=True) + rec_types = [ + RecommendationType.CF_DOT, + RecommendationType.CF_COSINE, + RecommendationType.CF_TIME_DOT, + RecommendationType.CF_TIME_COSINE, + RecommendationType.HOT_PROBLEM, + ] + limits = [100, 100, 100, 100, 20] + shuffle = True - res = [] - used_pid = set() + allow_debug_type = ( + self.request.user.is_impersonate or self.request.user.is_superuser + ) + if allow_debug_type and "debug_type" in self.request.GET: + try: + debug_type = int(self.request.GET.get("debug_type")) + except ValueError: + raise Http404() + rec_types = [debug_type] + limits = [100] + shuffle = False - for obj in merged_array: - if type(obj) == tuple: - obj = obj[1] - if obj not in used_pid: - res.append(obj) - used_pid.add(obj) - return res + return get_user_recommended_problems( + user_id, problem_ids, rec_types, limits, shuffle + ) def get_queryset(self): if self.feed_type == "volunteer": @@ -885,40 +895,8 @@ class ProblemFeed(ProblemList, FeedView): if not settings.ML_OUTPUT_PATH or not user: return queryset.order_by("?").add_i18n_name(self.request.LANGUAGE_CODE) - cf_model = CollabFilter("collab_filter") - cf_time_model = CollabFilter("collab_filter_time") + q = self.get_recommended_problem_ids(queryset) - queryset = queryset.values_list("id", flat=True) - hot_problems_recommendations = [ - problem.id - for problem in hot_problems(timedelta(days=7), 20) - if problem.id in set(queryset) - ] - - q = self.merge_recommendation( - [ - cf_model.user_recommendations(user, queryset, cf_model.DOT, 100), - cf_model.user_recommendations( - user, - queryset, - cf_model.COSINE, - 100, - ), - cf_time_model.user_recommendations( - user, - queryset, - cf_time_model.COSINE, - 100, - ), - cf_time_model.user_recommendations( - user, - queryset, - cf_time_model.DOT, - 100, - ), - hot_problems_recommendations, - ] - ) queryset = Problem.objects.filter(id__in=q) queryset = queryset.add_i18n_name(self.request.LANGUAGE_CODE) diff --git a/judge/views/problem_data.py b/judge/views/problem_data.py index 121d570..0525395 100644 --- a/judge/views/problem_data.py +++ b/judge/views/problem_data.py @@ -56,6 +56,7 @@ from judge.utils.fine_uploader import ( FineUploadForm, ) from judge.views.problem import ProblemMixin +from judge.logging import log_exception mimetypes.init() mimetypes.add_type("application/x-yaml", ".yml") @@ -249,6 +250,9 @@ class ProblemDataView(TitleMixin, ProblemManagerMixin): return ZipFile(data.zipfile.path).namelist() except BadZipfile: return [] + except FileNotFoundError as e: + log_exception(e) + return [] return [] def get_context_data(self, **kwargs): diff --git a/judge/views/resolver.py b/judge/views/resolver.py index cf8a193..84e5dff 100644 --- a/judge/views/resolver.py +++ b/judge/views/resolver.py @@ -1,6 +1,6 @@ from django.views.generic import TemplateView from django.utils.translation import gettext as _ -from django.http import HttpResponseForbidden +from django.http import HttpResponseForbidden, JsonResponse from judge.models import Contest from django.utils.safestring import mark_safe @@ -21,7 +21,7 @@ class Resolver(TemplateView): hidden_subtasks = self.contest.format.get_hidden_subtasks() num_problems = len(problems) problem_sub = [0] * num_problems - sub_frozen = [0] * num_problems + sub_frozen = [[] for _ in range(num_problems)] problems_json = {str(i): {} for i in range(1, num_problems + 1)} users = {} @@ -126,10 +126,8 @@ class Resolver(TemplateView): for i in hidden_subtasks: order = id_to_order[i] - if hidden_subtasks[i]: - sub_frozen[order - 1] = min(hidden_subtasks[i]) - else: - sub_frozen[order - 1] = problem_sub[order - 1] + 1 + sub_frozen[order - 1] = list(hidden_subtasks[i]) + return { "problem_sub": problem_sub, "sub_frozen": sub_frozen, @@ -143,8 +141,15 @@ class Resolver(TemplateView): return context def get(self, request, *args, **kwargs): - if request.user.is_superuser: - self.contest = Contest.objects.get(key=kwargs.get("contest")) - if self.contest.format.has_hidden_subtasks: - return super(Resolver, self).get(request, *args, **kwargs) - return HttpResponseForbidden() + if not request.user.is_superuser: + return HttpResponseForbidden() + self.contest = Contest.objects.get(key=kwargs.get("contest")) + if not self.contest.format.has_hidden_subtasks: + return HttpResponseForbidden() + + if self.request.GET.get("json"): + json_dumps_params = {"ensure_ascii": False} + return JsonResponse( + self.get_contest_json(), json_dumps_params=json_dumps_params + ) + return super(Resolver, self).get(request, *args, **kwargs) diff --git a/judge/views/submission.py b/judge/views/submission.py index eae961b..26300b0 100644 --- a/judge/views/submission.py +++ b/judge/views/submission.py @@ -1,6 +1,5 @@ import json import os.path -import zipfile from operator import attrgetter from django.conf import settings @@ -196,8 +195,8 @@ def get_cases_data(submission): continue count += 1 problem_data[count] = { - "input": case_data[case.input_file] if case.input_file else "", - "answer": case_data[case.output_file] if case.output_file else "", + "input": case_data.get(case.input_file, "") if case.input_file else "", + "answer": case_data.get(case.output_file, "") if case.output_file else "", } return problem_data diff --git a/locale/vi/LC_MESSAGES/django.po b/locale/vi/LC_MESSAGES/django.po index 4497f98..338f920 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: 2023-09-17 12:21+0700\n" +"POT-Creation-Date: 2023-11-02 08:11+0700\n" "PO-Revision-Date: 2021-07-20 03:44\n" "Last-Translator: Icyene\n" "Language-Team: Vietnamese\n" @@ -18,58 +18,75 @@ msgstr "" "X-Crowdin-Project-ID: 466004\n" "X-Crowdin-File-ID: 5\n" -#: chat_box/models.py:39 chat_box/models.py:64 chat_box/models.py:79 -#: judge/admin/interface.py:150 judge/models/contest.py:641 -#: judge/models/contest.py:847 judge/models/course.py:115 -#: judge/models/profile.py:390 judge/models/profile.py:468 +#: chat_box/models.py:54 chat_box/models.py:80 chat_box/models.py:96 +#: judge/admin/interface.py:150 judge/models/contest.py:647 +#: judge/models/contest.py:853 judge/models/course.py:115 +#: judge/models/profile.py:433 judge/models/profile.py:511 msgid "user" msgstr "người dùng" -#: chat_box/models.py:41 judge/models/comment.py:44 judge/models/comment.py:189 +#: chat_box/models.py:56 judge/models/comment.py:44 +#: judge/models/notification.py:17 msgid "posted time" msgstr "thời gian đăng" -#: chat_box/models.py:43 judge/models/comment.py:49 +#: chat_box/models.py:58 judge/models/comment.py:49 msgid "body of comment" msgstr "nội dung bình luận" -#: chat_box/models.py:68 +#: chat_box/models.py:84 msgid "last seen" msgstr "xem lần cuối" -#: chat_box/views.py:47 +#: chat_box/views.py:44 msgid "LQDOJ Chat" msgstr "" -#: dmoj/settings.py:365 +#: chat_box/views.py:417 +msgid "Recent" +msgstr "Gần đây" + +#: chat_box/views.py:421 templates/base.html:187 +#: templates/comments/content-list.html:78 +#: templates/contest/contest-list-tabs.html:4 +#: templates/contest/ranking-table.html:47 templates/internal/problem.html:62 +#: templates/organization/org-left-sidebar.html:12 +#: templates/problem/left-sidebar.html:6 +#: templates/problem/problem-list-tabs.html:6 +#: templates/submission/info-base.html:12 templates/submission/list.html:395 +#: templates/submission/submission-list-tabs.html:15 +msgid "Admin" +msgstr "Admin" + +#: dmoj/settings.py:364 msgid "Vietnamese" msgstr "Tiếng Việt" -#: dmoj/settings.py:366 +#: dmoj/settings.py:365 msgid "English" msgstr "" -#: dmoj/urls.py:108 +#: dmoj/urls.py:109 msgid "Activation key invalid" msgstr "Mã kích hoạt không hợp lệ" -#: dmoj/urls.py:113 +#: dmoj/urls.py:114 msgid "Register" msgstr "Đăng ký" -#: dmoj/urls.py:120 +#: dmoj/urls.py:121 msgid "Registration Completed" msgstr "Đăng ký hoàn thành" -#: dmoj/urls.py:128 +#: dmoj/urls.py:129 msgid "Registration not allowed" msgstr "Đăng ký không thành công" -#: dmoj/urls.py:136 +#: dmoj/urls.py:137 msgid "Login" msgstr "Đăng nhập" -#: dmoj/urls.py:224 templates/base.html:218 +#: dmoj/urls.py:225 templates/base.html:111 #: templates/organization/org-left-sidebar.html:2 msgid "Home" msgstr "Trang chủ" @@ -99,7 +116,7 @@ msgid "Included contests" msgstr "" #: judge/admin/contest.py:79 judge/admin/volunteer.py:54 -#: templates/contest/clarification.html:42 templates/contest/contest.html:100 +#: templates/contest/clarification.html:42 templates/contest/contest.html:105 #: templates/contest/moss.html:41 templates/internal/left-sidebar.html:2 #: templates/internal/problem.html:40 templates/problem/list.html:17 #: templates/problem/list.html:34 templates/problem/list.html:153 @@ -119,9 +136,9 @@ msgstr "" msgid "Details" msgstr "Chi tiết" -#: judge/admin/contest.py:185 templates/contest/list.html:265 -#: templates/contest/list.html:306 templates/contest/list.html:351 -#: templates/contest/list.html:388 +#: judge/admin/contest.py:185 templates/contest/list.html:263 +#: templates/contest/list.html:304 templates/contest/list.html:349 +#: templates/contest/list.html:386 msgid "Format" msgstr "Thể thức" @@ -130,23 +147,11 @@ msgstr "Thể thức" msgid "Rating" msgstr "" -#: templates/markdown_editor/markdown_editor.html:107 -msgid "Insert Image" -msgstr "Chèn hình ảnh" - -#: templates/markdown_editor/markdown_editor.html:110 -msgid "From the web" -msgstr "Từ web" - -#: templates/markdown_editor/markdown_editor.html:116 -msgid "From your computer" -msgstr "Từ máy tính của bạn" - #: judge/admin/contest.py:201 msgid "Access" msgstr "Truy cập" -#: judge/admin/contest.py:211 judge/admin/problem.py:218 +#: judge/admin/contest.py:211 judge/admin/problem.py:220 msgid "Justice" msgstr "Xử phạt" @@ -190,7 +195,7 @@ msgstr "Tính toán lại kết quả" msgid "username" msgstr "tên đăng nhập" -#: judge/admin/contest.py:503 templates/base.html:341 +#: judge/admin/contest.py:503 templates/base.html:239 msgid "virtual" msgstr "ảo" @@ -218,95 +223,96 @@ msgstr "" msgid "diff" msgstr "" -#: judge/admin/organization.py:61 judge/admin/problem.py:275 +#: judge/admin/organization.py:61 judge/admin/problem.py:277 #: judge/admin/profile.py:117 msgid "View on site" msgstr "Xem trên trang" -#: judge/admin/problem.py:53 +#: judge/admin/problem.py:55 msgid "Describe the changes you made (optional)" msgstr "Mô tả các thay đổi (tùy chọn)" -#: judge/admin/problem.py:109 +#: judge/admin/problem.py:111 msgid "Memory unit" msgstr "Đơn vị bộ nhớ" -#: judge/admin/problem.py:211 +#: judge/admin/problem.py:213 msgid "Social Media" msgstr "Mạng Xã Hội" -#: judge/admin/problem.py:214 +#: judge/admin/problem.py:216 msgid "Taxonomy" msgstr "" -#: judge/admin/problem.py:215 judge/admin/problem.py:438 -#: templates/contest/contest.html:101 templates/problem/data.html:533 +#: judge/admin/problem.py:217 judge/admin/problem.py:452 +#: templates/contest/contest.html:106 +#: templates/contest/contests_summary.html:41 templates/problem/data.html:533 #: templates/problem/list.html:22 templates/problem/list.html:48 #: templates/user/base-users-table.html:10 templates/user/user-about.html:36 #: templates/user/user-about.html:52 templates/user/user-problems.html:58 msgid "Points" msgstr "Điểm" -#: judge/admin/problem.py:216 +#: judge/admin/problem.py:218 msgid "Limits" msgstr "Giới hạn" -#: judge/admin/problem.py:217 judge/admin/submission.py:351 -#: templates/base.html:254 templates/stats/tab.html:4 +#: judge/admin/problem.py:219 judge/admin/submission.py:351 +#: templates/base.html:155 templates/stats/tab.html:4 #: templates/submission/list.html:347 msgid "Language" msgstr "Ngôn ngữ" -#: judge/admin/problem.py:219 +#: judge/admin/problem.py:221 msgid "History" msgstr "Lịch sử" -#: judge/admin/problem.py:271 templates/problem/list-base.html:99 +#: judge/admin/problem.py:273 templates/problem/list-base.html:93 msgid "Authors" msgstr "Các tác giả" -#: judge/admin/problem.py:292 +#: judge/admin/problem.py:294 #, python-format msgid "%d problem successfully marked as public." msgid_plural "%d problems successfully marked as public." msgstr[0] "%d bài tập đã được đánh dấu công khai." -#: judge/admin/problem.py:299 +#: judge/admin/problem.py:301 msgid "Mark problems as public" msgstr "Công khai bài tập" -#: judge/admin/problem.py:308 +#: judge/admin/problem.py:310 #, python-format msgid "%d problem successfully marked as private." msgid_plural "%d problems successfully marked as private." msgstr[0] "%d bài tập đã được đánh dấu riêng tư." -#: judge/admin/problem.py:315 +#: judge/admin/problem.py:317 msgid "Mark problems as private" msgstr "Đánh dấu các bài tập là riêng tư" -#: judge/admin/problem.py:432 judge/admin/submission.py:314 +#: judge/admin/problem.py:446 judge/admin/submission.py:314 #: templates/problem/list.html:18 templates/problem/list.html:37 msgid "Problem code" msgstr "Mã bài" -#: judge/admin/problem.py:444 judge/admin/submission.py:320 +#: judge/admin/problem.py:458 judge/admin/submission.py:320 msgid "Problem name" msgstr "Tên bài" -#: judge/admin/problem.py:450 +#: judge/admin/problem.py:464 #, fuzzy #| msgid "contest rating" msgid "Voter rating" msgstr "rating kỳ thi" -#: judge/admin/problem.py:456 +#: judge/admin/problem.py:470 #, fuzzy #| msgid "Total points" msgid "Voter point" msgstr "Tổng điểm" -#: judge/admin/problem.py:462 +#: judge/admin/problem.py:476 msgid "Vote" msgstr "" @@ -315,7 +321,7 @@ msgid "timezone" msgstr "múi giờ" #: judge/admin/profile.py:126 judge/admin/submission.py:327 -#: templates/notification/list.html:12 +#: templates/notification/list.html:9 #: templates/organization/requests/log.html:9 #: templates/organization/requests/pending.html:19 #: templates/ticket/list.html:263 @@ -412,9 +418,9 @@ msgstr[0] "%d bài nộp đã được tính điểm lại." msgid "Rescore the selected submissions" msgstr "Tính điểm lại cái bài nộp" -#: judge/admin/submission.py:332 templates/contest/list.html:250 -#: templates/contest/list.html:295 templates/contest/list.html:340 -#: templates/contest/list.html:382 templates/notification/list.html:15 +#: judge/admin/submission.py:332 templates/contest/list.html:248 +#: templates/contest/list.html:293 templates/contest/list.html:338 +#: templates/contest/list.html:380 templates/notification/list.html:12 #: templates/organization/requests/log.html:10 #: templates/organization/requests/pending.html:20 #: templates/problem/list.html:154 @@ -458,20 +464,20 @@ msgstr "Dạng" msgid "Online Judge" msgstr "" -#: judge/comments.py:64 +#: judge/comments.py:63 msgid "Comment body" msgstr "Nội dung bình luận" -#: judge/comments.py:70 judge/views/ticket.py:78 +#: judge/comments.py:69 judge/views/ticket.py:73 msgid "Your part is silent, little toad." msgstr "Bạn không được phép bình luận." -#: judge/comments.py:79 templates/comments/list.html:17 +#: judge/comments.py:78 templates/comments/list.html:17 msgid "" "You need to have solved at least one problem before your voice can be heard." msgstr "Bạn phải giải ít nhất một bài trước khi được phép bình luận." -#: judge/comments.py:123 +#: judge/comments.py:122 msgid "Posted comment" msgstr "Bình luận đã đăng" @@ -503,80 +509,79 @@ msgstr "IOI mới" msgid "Ultimate" msgstr "" -#: judge/forms.py:115 +#: judge/forms.py:113 msgid "File size exceeds the maximum allowed limit of 5MB." msgstr "File tải lên không được quá 5MB." -#: judge/forms.py:146 +#: judge/forms.py:144 msgid "Any judge" msgstr "" -#: judge/forms.py:353 +#: judge/forms.py:344 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:354 judge/views/stats.py:166 templates/stats/site.html:27 +#: judge/forms.py:345 judge/views/stats.py:166 templates/stats/site.html:27 msgid "New users" msgstr "Thành viên mới" -#: judge/forms.py:371 +#: judge/forms.py:362 #, 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:430 judge/views/register.py:30 +#: judge/forms.py:421 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:431 judge/views/email.py:22 +#: judge/forms.py:422 judge/views/email.py:22 #: templates/registration/registration_form.html:46 #: templates/registration/registration_form.html:60 #: templates/user/edit-profile.html:108 templates/user/import/table_csv.html:5 msgid "Password" msgstr "Mật khẩu" -#: judge/forms.py:457 +#: judge/forms.py:448 msgid "Two Factor Authentication tokens must be 6 decimal digits." msgstr "Two Factor Authentication phải chứa 6 chữ số." -#: judge/forms.py:470 templates/registration/totp_auth.html:32 +#: judge/forms.py:461 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:477 judge/models/problem.py:133 +#: judge/forms.py:468 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:484 +#: judge/forms.py:475 msgid "Problem with code already exists." msgstr "Mã bài đã tồn tại." -#: judge/forms.py:491 judge/models/contest.py:93 +#: judge/forms.py:482 judge/models/contest.py:95 msgid "Contest id must be ^[a-z0-9]+$" msgstr "Mã kỳ thi phải có dạng ^[a-z0-9]+$" -#: judge/forms.py:498 templates/problem/search-form.html:46 +#: judge/forms.py:489 templates/contest/clone.html:47 +#: templates/problem/search-form.html:39 msgid "Group" -msgstr "" +msgstr "Nhóm" -#: judge/forms.py:506 +#: judge/forms.py:497 msgid "Contest with key already exists." msgstr "Mã kỳ thi đã tồn tại." -#: judge/forms.py:514 +#: judge/forms.py:505 msgid "Group doesn't exist." -msgstr "" +msgstr "Nhóm không tồn tại." -#: judge/forms.py:516 -#, fuzzy -#| msgid "You do not have the permission to rejudge submissions." +#: judge/forms.py:507 msgid "You don't have permission in this group." -msgstr "Bạn không có quyền chấm lại bài." +msgstr "Bạn không có quyền trong nhóm này." -#: judge/forms.py:566 +#: judge/forms.py:557 msgid "This problem is duplicated." msgstr "Bài này bị lặp" @@ -586,7 +591,7 @@ msgid "N j, Y, g:i a" msgstr "g:i a j b, Y" #: judge/jinja2/datetime.py:26 templates/chat/message.html:13 -#: templates/comments/content-list.html:36 +#: templates/comments/content-list.html:33 #, python-brace-format msgid "{time}" msgstr "{time}" @@ -609,12 +614,12 @@ msgid "No such group" msgstr "Nhóm không tồn tại" #: judge/models/bookmark.py:16 judge/models/comment.py:171 -#: judge/models/pagevote.py:15 +#: judge/models/pagevote.py:16 msgid "associated page" msgstr "trang tương ứng" #: judge/models/bookmark.py:19 judge/models/comment.py:48 -#: judge/models/pagevote.py:18 judge/models/problem.py:713 +#: judge/models/pagevote.py:19 judge/models/problem.py:718 msgid "votes" msgstr "bình chọn" @@ -674,7 +679,7 @@ msgstr "ẩn bình luận" msgid "parent" msgstr "" -#: judge/models/comment.py:63 judge/models/comment.py:191 +#: judge/models/comment.py:63 judge/models/notification.py:31 msgid "comment" msgstr "bình luận" @@ -700,124 +705,117 @@ msgstr "" msgid "Override comment lock" msgstr "" -#: judge/models/comment.py:185 -msgid "owner" -msgstr "" - -#: judge/models/comment.py:193 judge/models/message.py:28 -msgid "read" -msgstr "" - -#: judge/models/comment.py:194 -msgid "category" -msgstr "" - -#: judge/models/comment.py:197 -msgid "html link to comments, used for non-comments" -msgstr "" - -#: judge/models/comment.py:203 -msgid "who trigger, used for non-comment" -msgstr "" - -#: judge/models/contest.py:40 +#: judge/models/contest.py:42 msgid "Invalid colour." msgstr "" -#: judge/models/contest.py:44 +#: judge/models/contest.py:46 msgid "tag name" msgstr "" -#: judge/models/contest.py:48 +#: judge/models/contest.py:50 msgid "Lowercase letters and hyphens only." msgstr "" -#: judge/models/contest.py:53 +#: judge/models/contest.py:55 msgid "tag colour" msgstr "" -#: judge/models/contest.py:55 +#: judge/models/contest.py:57 msgid "tag description" msgstr "" -#: judge/models/contest.py:76 +#: judge/models/contest.py:78 msgid "contest tag" msgstr "" -#: judge/models/contest.py:77 judge/models/contest.py:250 +#: judge/models/contest.py:79 judge/models/contest.py:255 msgid "contest tags" msgstr "nhãn kỳ thi" -#: judge/models/contest.py:85 +#: judge/models/contest.py:87 msgid "Visible" msgstr "Hiển thị" -#: judge/models/contest.py:86 +#: judge/models/contest.py:88 msgid "Hidden for duration of contest" msgstr "Ẩn trong thời gian kỳ thi" -#: judge/models/contest.py:87 +#: judge/models/contest.py:89 msgid "Hidden for duration of participation" msgstr "Ẩn trong thời gian tham gia" -#: judge/models/contest.py:91 +#: judge/models/contest.py:93 msgid "contest id" msgstr "ID kỳ thi" -#: judge/models/contest.py:96 +#: judge/models/contest.py:98 msgid "contest name" msgstr "tên kỳ thi" -#: judge/models/contest.py:100 +#: judge/models/contest.py:102 judge/models/interface.py:79 +#: judge/models/problem.py:672 +msgid "authors" +msgstr "tác giả" + +#: judge/models/contest.py:103 msgid "These users will be able to edit the contest." msgstr "Những người dùng này có quyền chỉnh sửa kỳ thi." -#: judge/models/contest.py:106 +#: judge/models/contest.py:108 judge/models/problem.py:154 +msgid "curators" +msgstr "quản lý" + +#: judge/models/contest.py:110 msgid "" "These users will be able to edit the contest, but will not be listed as " "authors." msgstr "Những người dùng này là tác giả và có quyền chỉnh sửa kỳ thi." -#: judge/models/contest.py:115 +#: judge/models/contest.py:118 judge/models/problem.py:164 +msgid "testers" +msgstr "" + +#: judge/models/contest.py:120 msgid "These users will be able to view the contest, but not edit it." msgstr "" "Những người dùng này có thể thấy kỳ thi nhưng không có quyền chỉnh sửa." -#: judge/models/contest.py:120 judge/models/course.py:158 +#: judge/models/contest.py:125 judge/models/course.py:158 #: judge/models/runtime.py:211 msgid "description" msgstr "mô tả" -#: judge/models/contest.py:122 judge/models/problem.py:583 +#: judge/models/contest.py:127 judge/models/problem.py:588 #: judge/models/runtime.py:216 msgid "problems" msgstr "bài tập" -#: judge/models/contest.py:124 judge/models/contest.py:646 +#: judge/models/contest.py:129 judge/models/contest.py:652 msgid "start time" msgstr "thời gian bắt đầu" -#: judge/models/contest.py:125 +#: judge/models/contest.py:130 msgid "end time" msgstr "thời gian kết thúc" -#: judge/models/contest.py:127 judge/models/problem.py:186 -#: judge/models/problem.py:618 +#: judge/models/contest.py:132 judge/models/problem.py:183 +#: judge/models/problem.py:623 msgid "time limit" msgstr "giới hạn thời gian" -#: judge/models/contest.py:131 +#: judge/models/contest.py:136 msgid "" "Format hh:mm:ss. For example, if you want a 2-hour contest, enter 02:00:00" msgstr "" "Định dạng hh:mm:ss (giờ:phút:giây). Ví dụ, nếu muốn tạo kỳ thi dài 2h, hãy " "nhập 02:00:00" -#: judge/models/contest.py:135 +#: judge/models/contest.py:140 msgid "freeze after" msgstr "đóng băng sau" -#: judge/models/contest.py:139 +#: judge/models/contest.py:144 msgid "" "Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, " "enter 02:00:00" @@ -825,12 +823,12 @@ 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:143 judge/models/course.py:28 -#: judge/models/course.py:164 judge/models/problem.py:225 +#: judge/models/contest.py:148 judge/models/course.py:28 +#: judge/models/course.py:164 judge/models/problem.py:222 msgid "publicly visible" msgstr "công khai" -#: judge/models/contest.py:146 +#: judge/models/contest.py:151 msgid "" "Should be set even for organization-private contests, where it determines " "whether the contest is visible to members of the specified organizations." @@ -838,92 +836,92 @@ msgstr "" "Đánh dấu ngay cả với các kỳ thi riêng tư của nhóm, quyết định việc kỳ thi có " "được hiển thị với các thành viên hay không." -#: judge/models/contest.py:152 +#: judge/models/contest.py:157 msgid "contest rated" msgstr "kỳ thi được xếp hạng" -#: judge/models/contest.py:153 +#: judge/models/contest.py:158 msgid "Whether this contest can be rated." msgstr "Quyết định kỳ thi có được xếp hạng không." -#: judge/models/contest.py:157 +#: judge/models/contest.py:162 msgid "scoreboard visibility" msgstr "khả năng hiển thị của bảng điểm" -#: judge/models/contest.py:160 +#: judge/models/contest.py:165 msgid "Scoreboard visibility through the duration of the contest" msgstr "Khả năng hiển thị của bảng điểm trong thời gian kỳ thi" -#: judge/models/contest.py:165 +#: judge/models/contest.py:170 msgid "view contest scoreboard" msgstr "xem bảng điểm kỳ thi" -#: judge/models/contest.py:168 +#: judge/models/contest.py:173 msgid "These users will be able to view the scoreboard." msgstr "Những người dùng này được phép xem bảng điểm." -#: judge/models/contest.py:171 +#: judge/models/contest.py:176 msgid "public scoreboard" msgstr "công khai bảng điểm" -#: judge/models/contest.py:172 +#: judge/models/contest.py:177 msgid "Ranking page is public even for private contests." msgstr "Trang xếp hạng được công khai, kể cả cho kỳ thi riêng tư." -#: judge/models/contest.py:176 +#: judge/models/contest.py:181 msgid "no comments" msgstr "không bình luận" -#: judge/models/contest.py:177 +#: judge/models/contest.py:182 msgid "Use clarification system instead of comments." msgstr "Dùng hệ thống thông báo thay vì bình luận." -#: judge/models/contest.py:182 +#: judge/models/contest.py:187 msgid "Rating floor for contest" msgstr "Cận dưới rating được xếp hạng trong kỳ thi" -#: judge/models/contest.py:188 +#: judge/models/contest.py:193 msgid "Rating ceiling for contest" msgstr "Cận trên rating được xếp hạng trong kỳ thi" -#: judge/models/contest.py:193 +#: judge/models/contest.py:198 msgid "rate all" msgstr "xếp hạng tất cả" -#: judge/models/contest.py:194 +#: judge/models/contest.py:199 msgid "Rate all users who joined." msgstr "Xếp hạng tất cả người dùng đã tham gia (kể cả không nộp)." -#: judge/models/contest.py:199 +#: judge/models/contest.py:204 msgid "exclude from ratings" msgstr "không xếp hạng" -#: judge/models/contest.py:204 +#: judge/models/contest.py:209 msgid "private to specific users" msgstr "riêng tư với các người dùng này" -#: judge/models/contest.py:209 +#: judge/models/contest.py:214 msgid "private contestants" msgstr "thí sinh riêng tư" -#: judge/models/contest.py:210 +#: judge/models/contest.py:215 msgid "If private, only these users may see the contest" msgstr "Nếu riêng tư, chỉ những người dùng này mới thấy kỳ thi" -#: judge/models/contest.py:214 +#: judge/models/contest.py:219 msgid "hide problem tags" msgstr "ẩn nhãn kỳ thi" -#: judge/models/contest.py:215 +#: judge/models/contest.py:220 msgid "Whether problem tags should be hidden by default." msgstr "" "Quyết định việc nhãn bài tập (DP, Tham lam, ...) được ẩn trong kỳ thi không." -#: judge/models/contest.py:219 +#: judge/models/contest.py:224 msgid "run pretests only" msgstr "chỉ chạy pretests" -#: judge/models/contest.py:221 +#: judge/models/contest.py:226 msgid "" "Whether judges should grade pretests only, versus all testcases. Commonly " "set during a contest, then unset prior to rejudging user submissions when " @@ -932,51 +930,51 @@ msgstr "" "Quyết định việc các máy chấm chỉ chấm pretests thay vì tất cả các test. Sau " "kỳ thi, hãy bỏ đánh dấu ô này và chấm lại tất cả các bài." -#: judge/models/contest.py:228 judge/models/interface.py:96 -#: judge/models/problem.py:285 +#: judge/models/contest.py:233 judge/models/interface.py:96 +#: judge/models/problem.py:282 msgid "private to organizations" msgstr "riêng tư với các tổ chức" -#: judge/models/contest.py:233 judge/models/course.py:34 -#: judge/models/interface.py:92 judge/models/problem.py:281 -#: judge/models/profile.py:144 +#: judge/models/contest.py:238 judge/models/course.py:34 +#: judge/models/interface.py:92 judge/models/problem.py:278 +#: judge/models/profile.py:149 msgid "organizations" msgstr "tổ chức" -#: judge/models/contest.py:234 +#: judge/models/contest.py:239 msgid "If private, only these organizations may see the contest" msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được kỳ thi" -#: judge/models/contest.py:237 judge/models/problem.py:256 +#: judge/models/contest.py:242 judge/models/problem.py:253 msgid "OpenGraph image" msgstr "Hình ảnh OpenGraph" -#: judge/models/contest.py:240 judge/models/profile.py:92 +#: judge/models/contest.py:245 judge/models/profile.py:97 msgid "Logo override image" msgstr "Hình ảnh ghi đè logo" -#: judge/models/contest.py:245 +#: judge/models/contest.py:250 msgid "" "This image will replace the default site logo for users inside the contest." msgstr "Ảnh này sẽ thay thế cho logo mặc định trong kỳ thi." -#: judge/models/contest.py:253 +#: judge/models/contest.py:258 msgid "the amount of live participants" msgstr "số lượng thí sinh thi trực tiếp" -#: judge/models/contest.py:257 +#: judge/models/contest.py:262 msgid "contest summary" msgstr "tổng kết kỳ thi" -#: judge/models/contest.py:259 judge/models/problem.py:262 +#: judge/models/contest.py:264 judge/models/problem.py:259 msgid "Plain-text, shown in meta description tag, e.g. for social media." msgstr "" -#: judge/models/contest.py:263 judge/models/profile.py:87 +#: judge/models/contest.py:268 judge/models/profile.py:92 msgid "access code" msgstr "mật khẩu truy cập" -#: judge/models/contest.py:268 +#: judge/models/contest.py:273 msgid "" "An optional code to prompt contestants before they are allowed to join the " "contest. Leave it blank to disable." @@ -984,302 +982,314 @@ msgstr "" "Mật khẩu truy cập cho các thí sinh muốn tham gia kỳ thi. Để trống nếu không " "dùng." -#: judge/models/contest.py:274 judge/models/problem.py:244 +#: judge/models/contest.py:279 judge/models/problem.py:241 msgid "personae non gratae" msgstr "Chặn tham gia" -#: judge/models/contest.py:276 +#: judge/models/contest.py:281 msgid "Bans the selected users from joining this contest." msgstr "Cấm những người dùng được chọn tham gia kỳ thi." -#: judge/models/contest.py:279 +#: judge/models/contest.py:284 msgid "contest format" msgstr "format kỳ thi" -#: judge/models/contest.py:283 +#: judge/models/contest.py:288 msgid "The contest format module to use." msgstr "Format kỳ thi sử dụng." -#: judge/models/contest.py:286 +#: judge/models/contest.py:291 msgid "contest format configuration" msgstr "Tùy chỉnh format kỳ thi" -#: judge/models/contest.py:290 +#: judge/models/contest.py:295 msgid "" "A JSON object to serve as the configuration for the chosen contest format " "module. Leave empty to use None. Exact format depends on the contest format " "selected." msgstr "" -#: judge/models/contest.py:303 +#: judge/models/contest.py:308 msgid "precision points" msgstr "Hiển thị điểm" -#: judge/models/contest.py:306 +#: judge/models/contest.py:311 msgid "Number of digits to round points to." msgstr "Số chữ số thập phân trên bảng điểm." -#: judge/models/contest.py:614 +#: judge/models/contest.py:620 msgid "See private contests" msgstr "" -#: judge/models/contest.py:615 +#: judge/models/contest.py:621 msgid "Edit own contests" msgstr "" -#: judge/models/contest.py:616 +#: judge/models/contest.py:622 msgid "Edit all contests" msgstr "" -#: judge/models/contest.py:617 +#: judge/models/contest.py:623 msgid "Clone contest" msgstr "" -#: judge/models/contest.py:618 templates/contest/moss.html:72 +#: judge/models/contest.py:624 templates/contest/moss.html:72 msgid "MOSS contest" msgstr "" -#: judge/models/contest.py:619 +#: judge/models/contest.py:625 msgid "Rate contests" msgstr "" -#: judge/models/contest.py:620 +#: judge/models/contest.py:626 msgid "Contest access codes" msgstr "" -#: judge/models/contest.py:621 +#: judge/models/contest.py:627 msgid "Create private contests" msgstr "" -#: judge/models/contest.py:622 +#: judge/models/contest.py:628 msgid "Change contest visibility" msgstr "" -#: judge/models/contest.py:623 +#: judge/models/contest.py:629 msgid "Edit contest problem label script" msgstr "Cách hiển thị thứ tự bài tập" -#: judge/models/contest.py:625 judge/models/contest.py:772 -#: judge/models/contest.py:850 judge/models/contest.py:880 +#: judge/models/contest.py:631 judge/models/contest.py:778 +#: judge/models/contest.py:856 judge/models/contest.py:886 #: judge/models/course.py:178 judge/models/submission.py:116 msgid "contest" msgstr "kỳ thi" -#: judge/models/contest.py:626 +#: judge/models/contest.py:632 msgid "contests" msgstr "kỳ thi" -#: judge/models/contest.py:635 +#: judge/models/contest.py:641 msgid "associated contest" msgstr "" -#: judge/models/contest.py:648 +#: judge/models/contest.py:654 msgid "score" msgstr "điểm" -#: judge/models/contest.py:649 +#: judge/models/contest.py:655 msgid "cumulative time" msgstr "tổng thời gian" -#: judge/models/contest.py:651 +#: judge/models/contest.py:657 msgid "is disqualified" msgstr "đã bị loại" -#: judge/models/contest.py:653 +#: judge/models/contest.py:659 msgid "Whether this participation is disqualified." msgstr "Quyết định thí sinh có bị loại không." -#: judge/models/contest.py:655 +#: judge/models/contest.py:661 msgid "tie-breaking field" msgstr "" -#: judge/models/contest.py:657 +#: judge/models/contest.py:663 msgid "virtual participation id" msgstr "id lần tham gia ảo" -#: judge/models/contest.py:659 +#: judge/models/contest.py:665 msgid "0 means non-virtual, otherwise the n-th virtual participation." msgstr "0 nghĩa là tham gia chính thức, ngược lại là lần tham gia ảo thứ n." -#: judge/models/contest.py:662 +#: judge/models/contest.py:668 msgid "contest format specific data" msgstr "" -#: judge/models/contest.py:665 +#: judge/models/contest.py:671 msgid "same as format_data, but including frozen results" msgstr "" -#: judge/models/contest.py:669 +#: judge/models/contest.py:675 #, fuzzy #| msgid "score" msgid "final score" msgstr "điểm" -#: judge/models/contest.py:671 +#: judge/models/contest.py:677 #, fuzzy #| msgid "cumulative time" msgid "final cumulative time" msgstr "tổng thời gian" -#: judge/models/contest.py:747 +#: judge/models/contest.py:753 #, python-format msgid "%s spectating in %s" msgstr "%s đang theo dõi trong %s" -#: judge/models/contest.py:752 +#: judge/models/contest.py:758 #, python-format msgid "%s in %s, v%d" msgstr "%s trong %s, v%d" -#: judge/models/contest.py:757 +#: judge/models/contest.py:763 #, python-format msgid "%s in %s" msgstr "%s trong %s" -#: judge/models/contest.py:760 +#: judge/models/contest.py:766 msgid "contest participation" msgstr "lần tham gia kỳ thi" -#: judge/models/contest.py:761 +#: judge/models/contest.py:767 msgid "contest participations" msgstr "lần tham gia kỳ thi" -#: judge/models/contest.py:768 judge/models/contest.py:821 -#: judge/models/contest.py:883 judge/models/problem.py:582 -#: judge/models/problem.py:589 judge/models/problem.py:610 -#: judge/models/problem.py:641 judge/models/problem_data.py:50 +#: judge/models/contest.py:774 judge/models/contest.py:827 +#: judge/models/contest.py:889 judge/models/problem.py:587 +#: judge/models/problem.py:594 judge/models/problem.py:615 +#: judge/models/problem.py:646 judge/models/problem_data.py:50 msgid "problem" msgstr "bài tập" -#: judge/models/contest.py:776 judge/models/contest.py:833 -#: judge/models/course.py:182 judge/models/problem.py:209 +#: judge/models/contest.py:782 judge/models/contest.py:839 +#: judge/models/course.py:182 judge/models/problem.py:206 msgid "points" msgstr "điểm" -#: judge/models/contest.py:777 +#: judge/models/contest.py:783 msgid "partial" msgstr "thành phần" -#: judge/models/contest.py:778 judge/models/contest.py:835 +#: judge/models/contest.py:784 judge/models/contest.py:841 msgid "is pretested" msgstr "dùng pretest" -#: judge/models/contest.py:779 judge/models/interface.py:47 +#: judge/models/contest.py:785 judge/models/interface.py:47 msgid "order" msgstr "thứ tự" -#: judge/models/contest.py:781 +#: judge/models/contest.py:787 msgid "visible testcases" msgstr "hiển thị test" -#: judge/models/contest.py:786 +#: judge/models/contest.py:792 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:788 +#: judge/models/contest.py:794 msgid "max submissions" msgstr "số lần nộp tối đa" -#: judge/models/contest.py:791 +#: judge/models/contest.py:797 msgid "Why include a problem you can't submit to?" msgstr "" -#: judge/models/contest.py:795 +#: judge/models/contest.py:801 #, fuzzy #| msgid "Only for format new IOI. Separated by commas, e.g: 2, 3" msgid "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" -#: judge/models/contest.py:796 +#: judge/models/contest.py:802 #, fuzzy #| msgid "frozen subtasks" msgid "hidden subtasks" msgstr "Đóng băng subtasks" -#: judge/models/contest.py:808 +#: judge/models/contest.py:814 msgid "contest problem" msgstr "bài trong kỳ thi" -#: judge/models/contest.py:809 +#: judge/models/contest.py:815 msgid "contest problems" msgstr "bài trong kỳ thi" -#: judge/models/contest.py:815 judge/models/submission.py:233 +#: judge/models/contest.py:821 judge/models/submission.py:273 msgid "submission" msgstr "bài nộp" -#: judge/models/contest.py:828 judge/models/contest.py:854 +#: judge/models/contest.py:834 judge/models/contest.py:860 msgid "participation" msgstr "lần tham gia" -#: judge/models/contest.py:836 +#: judge/models/contest.py:842 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:841 +#: judge/models/contest.py:847 msgid "contest submission" msgstr "bài nộp kỳ thi" -#: judge/models/contest.py:842 +#: judge/models/contest.py:848 msgid "contest submissions" msgstr "bài nộp kỳ thi" -#: judge/models/contest.py:858 +#: judge/models/contest.py:864 msgid "rank" msgstr "rank" -#: judge/models/contest.py:859 +#: judge/models/contest.py:865 msgid "rating" msgstr "rating" -#: judge/models/contest.py:860 +#: judge/models/contest.py:866 msgid "raw rating" msgstr "rating thật" -#: judge/models/contest.py:861 +#: judge/models/contest.py:867 msgid "contest performance" msgstr "" -#: judge/models/contest.py:862 +#: judge/models/contest.py:868 msgid "last rated" msgstr "lần cuối được xếp hạng" -#: judge/models/contest.py:866 +#: judge/models/contest.py:872 msgid "contest rating" msgstr "rating kỳ thi" -#: judge/models/contest.py:867 +#: judge/models/contest.py:873 msgid "contest ratings" msgstr "rating kỳ thi" -#: judge/models/contest.py:891 +#: judge/models/contest.py:897 msgid "contest moss result" msgstr "kết quả MOSS kỳ thi" -#: judge/models/contest.py:892 +#: judge/models/contest.py:898 msgid "contest moss results" msgstr "kết quả MOSS kỳ thi" -#: judge/models/contest.py:897 +#: judge/models/contest.py:903 msgid "clarified problem" msgstr "" -#: judge/models/contest.py:899 +#: judge/models/contest.py:905 msgid "clarification body" msgstr "" -#: judge/models/contest.py:901 +#: judge/models/contest.py:907 msgid "clarification timestamp" msgstr "" +#: judge/models/contest.py:925 +#, fuzzy +#| msgid "contest summary" +msgid "contests summary" +msgstr "tổng kết kỳ thi" + +#: judge/models/contest.py:926 +#, fuzzy +#| msgid "contest summary" +msgid "contests summaries" +msgstr "tổng kết kỳ thi" + #: judge/models/course.py:21 #, fuzzy #| msgid "username" msgid "course name" msgstr "tên đăng nhập" -#: judge/models/course.py:23 judge/models/profile.py:53 +#: judge/models/course.py:23 judge/models/profile.py:58 msgid "organization description" msgstr "mô tả tổ chức" @@ -1305,7 +1315,7 @@ msgstr "" msgid "Course name shown in URL" msgstr "Tên được hiển thị trong đường dẫn" -#: judge/models/course.py:43 judge/models/profile.py:45 +#: judge/models/course.py:43 judge/models/profile.py:50 msgid "Only alphanumeric and hyphens" msgstr "" @@ -1382,15 +1392,11 @@ msgstr "mục cha" msgid "post title" msgstr "tiêu đề bài đăng" -#: judge/models/interface.py:79 judge/models/problem.py:667 -msgid "authors" -msgstr "tác giả" - #: judge/models/interface.py:80 msgid "slug" msgstr "slug" -#: judge/models/interface.py:81 judge/models/problem.py:665 +#: judge/models/interface.py:81 judge/models/problem.py:670 msgid "public visibility" msgstr "khả năng hiển thị công khai" @@ -1450,168 +1456,180 @@ msgstr "người nhận" msgid "message timestamp" msgstr "thời gian gửi" +#: judge/models/message.py:28 judge/models/notification.py:33 +msgid "read" +msgstr "" + #: judge/models/message.py:33 msgid "messages in the thread" msgstr "tin nhắn trong chuỗi" -#: judge/models/pagevote.py:24 +#: judge/models/notification.py:13 +msgid "owner" +msgstr "" + +#: judge/models/notification.py:18 +msgid "category" +msgstr "" + +#: judge/models/notification.py:21 +msgid "html link to comments, used for non-comments" +msgstr "" + +#: judge/models/notification.py:27 +msgid "who trigger, used for non-comment" +msgstr "" + +#: judge/models/pagevote.py:25 #, fuzzy #| msgid "votes" msgid "pagevote" msgstr "bình chọn" -#: judge/models/pagevote.py:25 +#: judge/models/pagevote.py:26 #, fuzzy #| msgid "votes" msgid "pagevotes" msgstr "bình chọn" -#: judge/models/pagevote.py:49 +#: judge/models/pagevote.py:51 #, fuzzy #| msgid "volunteer vote" msgid "pagevote vote" msgstr "vote từ TNV" -#: judge/models/pagevote.py:50 +#: judge/models/pagevote.py:52 #, fuzzy #| msgid "volunteer votes" msgid "pagevote votes" msgstr "vote từ TNV" -#: judge/models/problem.py:44 +#: judge/models/problem.py:43 msgid "problem category ID" msgstr "mã của nhóm bài" -#: judge/models/problem.py:47 +#: judge/models/problem.py:46 msgid "problem category name" msgstr "tên nhóm bài" -#: judge/models/problem.py:55 +#: judge/models/problem.py:54 msgid "problem type" msgstr "dạng bài" -#: judge/models/problem.py:56 judge/models/problem.py:176 +#: judge/models/problem.py:55 judge/models/problem.py:173 #: judge/models/volunteer.py:28 msgid "problem types" msgstr "dạng bài" -#: judge/models/problem.py:61 +#: judge/models/problem.py:60 msgid "problem group ID" msgstr "mã của nhóm bài" -#: judge/models/problem.py:63 +#: judge/models/problem.py:62 msgid "problem group name" msgstr "tên nhóm bài" -#: judge/models/problem.py:70 judge/models/problem.py:181 +#: judge/models/problem.py:69 judge/models/problem.py:178 msgid "problem group" msgstr "nhóm bài" -#: judge/models/problem.py:71 +#: judge/models/problem.py:70 msgid "problem groups" msgstr "nhóm bài" -#: judge/models/problem.py:78 +#: judge/models/problem.py:77 msgid "key" msgstr "" -#: judge/models/problem.py:81 +#: judge/models/problem.py:80 msgid "link" msgstr "đường dẫn" -#: judge/models/problem.py:82 +#: judge/models/problem.py:81 msgid "full name" msgstr "tên đầy đủ" -#: judge/models/problem.py:86 judge/models/profile.py:50 +#: judge/models/problem.py:85 judge/models/profile.py:55 #: judge/models/runtime.py:34 msgid "short name" msgstr "tên ngắn" -#: judge/models/problem.py:87 +#: judge/models/problem.py:86 msgid "Displayed on pages under this license" msgstr "Được hiển thị trên các trang theo giấy phép này" -#: judge/models/problem.py:92 +#: judge/models/problem.py:91 msgid "icon" msgstr "icon" -#: judge/models/problem.py:93 +#: judge/models/problem.py:92 msgid "URL to the icon" msgstr "Đường dẫn icon" -#: judge/models/problem.py:95 +#: judge/models/problem.py:94 msgid "license text" msgstr "văn bản giấy phép" -#: judge/models/problem.py:104 +#: judge/models/problem.py:103 msgid "license" msgstr "" -#: judge/models/problem.py:105 +#: judge/models/problem.py:104 msgid "licenses" msgstr "" -#: judge/models/problem.py:130 +#: judge/models/problem.py:127 msgid "problem code" msgstr "mã bài" -#: judge/models/problem.py:136 +#: judge/models/problem.py:133 msgid "A short, unique code for the problem, used in the url after /problem/" msgstr "Mã bài ngắn, độc nhất cho bài tập, được dùng trong url sau /problem/" -#: judge/models/problem.py:141 +#: judge/models/problem.py:138 msgid "problem name" msgstr "Tên bài" -#: judge/models/problem.py:143 +#: judge/models/problem.py:140 msgid "The full name of the problem, as shown in the problem list." msgstr "Tên đầy đủ của bài, như được hiển thị trên danh sách bài tập" -#: judge/models/problem.py:145 +#: judge/models/problem.py:142 msgid "problem body" msgstr "Nội dung" -#: judge/models/problem.py:148 +#: judge/models/problem.py:145 msgid "creators" msgstr "" -#: judge/models/problem.py:152 +#: judge/models/problem.py:149 msgid "These users will be able to edit the problem, and be listed as authors." msgstr "" "Những người dùng này sẽ có thể chỉnh sửa bài tập, và nằm trong danh sách các " "tác giả" -#: judge/models/problem.py:157 -msgid "curators" -msgstr "" - -#: judge/models/problem.py:161 +#: judge/models/problem.py:158 msgid "" "These users will be able to edit the problem, but not be listed as authors." msgstr "" "Những người dùng này sẽ có thể chỉnh sửa bài tập, nhưng không nằm trong danh " "sách các tác giả" -#: judge/models/problem.py:167 -msgid "testers" -msgstr "" - -#: judge/models/problem.py:171 +#: judge/models/problem.py:168 msgid "These users will be able to view the private problem, but not edit it." msgstr "" "Những người dùng này sẽ thấy được bài tập này (dù riêng tư), nhưng không " "chỉnh sửa được" -#: judge/models/problem.py:177 judge/models/volunteer.py:29 +#: judge/models/problem.py:174 judge/models/volunteer.py:29 msgid "The type of problem, as shown on the problem's page." msgstr "Dạng bài, giống như trên trang bài tập" -#: judge/models/problem.py:183 +#: judge/models/problem.py:180 msgid "The group of problem, shown under Category in the problem list." msgstr "Nhóm bài, hiện ở mục Nhóm bài trong danh sách bài tập" -#: judge/models/problem.py:188 +#: judge/models/problem.py:185 msgid "" "The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) " "are supported." @@ -1619,11 +1637,11 @@ msgstr "" "Giới hạn thời gian cho bài tập này, theo đơn vị giây. Có thể nhập số thực " "(ví dụ 1.5)" -#: judge/models/problem.py:197 judge/models/problem.py:625 +#: judge/models/problem.py:194 judge/models/problem.py:630 msgid "memory limit" msgstr "Giới hạn bộ nhớ" -#: judge/models/problem.py:199 +#: judge/models/problem.py:196 msgid "" "The memory limit for this problem, in kilobytes (e.g. 256mb = 262144 " "kilobytes)." @@ -1631,7 +1649,7 @@ msgstr "" "Giới hạn bộ nhớ cho bài này, theo đơn vị kilobytes (ví dụ 256mb = 262144 " "kilobytes)" -#: judge/models/problem.py:211 +#: judge/models/problem.py:208 msgid "" "Points awarded for problem completion. Points are displayed with a 'p' " "suffix if partial." @@ -1639,148 +1657,148 @@ msgstr "" "Điểm thưởng khi hoàn thành bài tập. Điểm có thêm chữ 'p' ở sau cùng nếu như " "chấp nhận cho điểm thành phần (có điểm ngay khi không đúng toàn bộ test)" -#: judge/models/problem.py:217 +#: judge/models/problem.py:214 msgid "allows partial points" msgstr "cho phép điểm thành phần" -#: judge/models/problem.py:221 +#: judge/models/problem.py:218 msgid "allowed languages" msgstr "các ngôn ngữ được cho phép" -#: judge/models/problem.py:222 +#: judge/models/problem.py:219 msgid "List of allowed submission languages." msgstr "Danh sách các ngôn ngữ lập trình cho phép" -#: judge/models/problem.py:228 +#: judge/models/problem.py:225 msgid "manually managed" msgstr "" -#: judge/models/problem.py:231 +#: judge/models/problem.py:228 msgid "Whether judges should be allowed to manage data or not." msgstr "" -#: judge/models/problem.py:234 +#: judge/models/problem.py:231 msgid "date of publishing" msgstr "Ngày công bố" -#: judge/models/problem.py:239 +#: judge/models/problem.py:236 msgid "" "Doesn't have magic ability to auto-publish due to backward compatibility" msgstr "" -#: judge/models/problem.py:246 +#: judge/models/problem.py:243 msgid "Bans the selected users from submitting to this problem." msgstr "Cấm những người dùng được chọn nộp bài tập này." -#: judge/models/problem.py:253 +#: judge/models/problem.py:250 msgid "The license under which this problem is published." msgstr "Giấy phép xuất bản bài tập" -#: judge/models/problem.py:260 +#: judge/models/problem.py:257 msgid "problem summary" msgstr "Tóm tắt bài tập" -#: judge/models/problem.py:266 +#: judge/models/problem.py:263 msgid "number of users" msgstr "" -#: judge/models/problem.py:268 +#: judge/models/problem.py:265 msgid "The number of users who solved the problem." msgstr "Số lượng người dùng đã giải được bài" -#: judge/models/problem.py:270 +#: judge/models/problem.py:267 msgid "solve rate" msgstr "Tỉ lệ giải đúng" -#: judge/models/problem.py:282 +#: judge/models/problem.py:279 msgid "If private, only these organizations may see the problem." msgstr "Nếu bài riêng tư, chỉ những tổ chức này thấy được" -#: judge/models/problem.py:288 +#: judge/models/problem.py:285 msgid "pdf statement" msgstr "Đề bài bằng file pdf" -#: judge/models/problem.py:594 judge/models/problem.py:615 -#: judge/models/problem.py:646 judge/models/runtime.py:161 +#: judge/models/problem.py:599 judge/models/problem.py:620 +#: judge/models/problem.py:651 judge/models/runtime.py:161 msgid "language" msgstr "" -#: judge/models/problem.py:597 +#: judge/models/problem.py:602 msgid "translated name" msgstr "" -#: judge/models/problem.py:599 +#: judge/models/problem.py:604 msgid "translated description" msgstr "" -#: judge/models/problem.py:603 +#: judge/models/problem.py:608 msgid "problem translation" msgstr "" -#: judge/models/problem.py:604 +#: judge/models/problem.py:609 msgid "problem translations" msgstr "" -#: judge/models/problem.py:634 +#: judge/models/problem.py:639 msgid "language-specific resource limit" msgstr "" -#: judge/models/problem.py:635 +#: judge/models/problem.py:640 msgid "language-specific resource limits" msgstr "" -#: judge/models/problem.py:648 judge/models/submission.py:249 +#: judge/models/problem.py:653 judge/models/submission.py:289 msgid "source code" msgstr "mã nguồn" -#: judge/models/problem.py:652 +#: judge/models/problem.py:657 msgid "language-specific template" msgstr "" -#: judge/models/problem.py:653 +#: judge/models/problem.py:658 msgid "language-specific templates" msgstr "" -#: judge/models/problem.py:660 +#: judge/models/problem.py:665 msgid "associated problem" msgstr "" -#: judge/models/problem.py:666 +#: judge/models/problem.py:671 msgid "publish date" msgstr "" -#: judge/models/problem.py:668 +#: judge/models/problem.py:673 msgid "editorial content" msgstr "nội dung lời giải" -#: judge/models/problem.py:681 +#: judge/models/problem.py:686 #, python-format msgid "Editorial for %s" msgstr "" -#: judge/models/problem.py:685 +#: judge/models/problem.py:690 msgid "solution" msgstr "lời giải" -#: judge/models/problem.py:686 +#: judge/models/problem.py:691 msgid "solutions" msgstr "lời giải" -#: judge/models/problem.py:691 +#: judge/models/problem.py:696 #, fuzzy #| msgid "point value" msgid "proposed point value" msgstr "điểm" -#: judge/models/problem.py:692 +#: judge/models/problem.py:697 msgid "The amount of points you think this problem deserves." msgstr "Bạn nghĩ bài này đáng bao nhiêu điểm?" -#: judge/models/problem.py:706 +#: judge/models/problem.py:711 msgid "The time this vote was cast" msgstr "" -#: judge/models/problem.py:712 +#: judge/models/problem.py:717 msgid "vote" msgstr "" @@ -1802,7 +1820,7 @@ msgstr "Số thực (chênh lệch tương đối)" #: judge/models/problem_data.py:36 msgid "Non-trailing spaces" -msgstr "Không cho phép dấu cách cuối dòng" +msgstr "Không xét dấu cách cuối dòng" #: judge/models/problem_data.py:37 msgid "Unordered" @@ -1952,214 +1970,214 @@ msgstr "điểm" msgid "case is pretest?" msgstr "test là pretest?" -#: judge/models/profile.py:38 +#: judge/models/profile.py:43 msgid "organization title" msgstr "tiêu đề tổ chức" -#: judge/models/profile.py:41 +#: judge/models/profile.py:46 msgid "organization slug" msgstr "tên ngắn đường dẫn" -#: judge/models/profile.py:42 +#: judge/models/profile.py:47 msgid "Organization name shown in URL" msgstr "Tên được hiển thị trong đường dẫn" -#: judge/models/profile.py:51 +#: judge/models/profile.py:56 msgid "Displayed beside user name during contests" msgstr "Hiển thị bên cạnh tên người dùng trong kỳ thi" -#: judge/models/profile.py:56 +#: judge/models/profile.py:61 msgid "registrant" msgstr "người tạo" -#: judge/models/profile.py:59 +#: judge/models/profile.py:64 msgid "User who registered this organization" msgstr "Người tạo tổ chức" -#: judge/models/profile.py:63 +#: judge/models/profile.py:68 msgid "administrators" msgstr "người quản lý" -#: judge/models/profile.py:65 +#: judge/models/profile.py:70 msgid "Those who can edit this organization" msgstr "Những người có thể chỉnh sửa tổ chức" -#: judge/models/profile.py:68 +#: judge/models/profile.py:73 msgid "creation date" msgstr "ngày tạo" -#: judge/models/profile.py:71 +#: judge/models/profile.py:76 msgid "is open organization?" msgstr "tổ chức mở?" -#: judge/models/profile.py:72 +#: judge/models/profile.py:77 msgid "Allow joining organization" msgstr "Cho phép mọi người tham gia tổ chức" -#: judge/models/profile.py:76 +#: judge/models/profile.py:81 msgid "maximum size" msgstr "số lượng thành viên tối đa" -#: judge/models/profile.py:80 +#: judge/models/profile.py:85 msgid "" "Maximum amount of users in this organization, only applicable to private " "organizations" msgstr "Số người tối đa trong tổ chức, chỉ áp dụng với tổ chức riêng tư" -#: judge/models/profile.py:86 +#: judge/models/profile.py:91 msgid "Student access code" msgstr "Mã truy cập cho học sinh" -#: judge/models/profile.py:97 +#: judge/models/profile.py:102 msgid "" "This image will replace the default site logo for users viewing the " "organization." msgstr "Ảnh này sẽ thay thế logo mặc định khi ở trong tổ chức." -#: judge/models/profile.py:143 judge/models/profile.py:172 -#: judge/models/profile.py:396 judge/models/profile.py:475 +#: judge/models/profile.py:148 judge/models/profile.py:178 +#: judge/models/profile.py:439 judge/models/profile.py:518 msgid "organization" msgstr "" -#: judge/models/profile.py:149 +#: judge/models/profile.py:155 msgid "user associated" msgstr "" -#: judge/models/profile.py:151 +#: judge/models/profile.py:157 msgid "self-description" msgstr "" -#: judge/models/profile.py:154 +#: judge/models/profile.py:160 msgid "location" msgstr "" -#: judge/models/profile.py:160 +#: judge/models/profile.py:166 msgid "preferred language" msgstr "" -#: judge/models/profile.py:168 +#: judge/models/profile.py:174 msgid "last access time" msgstr "" -#: judge/models/profile.py:169 +#: judge/models/profile.py:175 msgid "last IP" msgstr "" -#: judge/models/profile.py:180 +#: judge/models/profile.py:186 msgid "display rank" msgstr "" -#: judge/models/profile.py:189 +#: judge/models/profile.py:195 msgid "comment mute" msgstr "" -#: judge/models/profile.py:190 +#: judge/models/profile.py:196 msgid "Some users are at their best when silent." msgstr "" -#: judge/models/profile.py:194 +#: judge/models/profile.py:200 msgid "unlisted user" msgstr "" -#: judge/models/profile.py:195 +#: judge/models/profile.py:201 msgid "User will not be ranked." msgstr "" -#: judge/models/profile.py:199 +#: judge/models/profile.py:205 #, fuzzy #| msgid "Banned from joining" msgid "banned from voting" msgstr "Bị cấm tham gia" -#: judge/models/profile.py:200 +#: judge/models/profile.py:206 msgid "User will not be able to vote on problems' point values." msgstr "" -#: judge/models/profile.py:205 +#: judge/models/profile.py:211 msgid "user script" msgstr "" -#: judge/models/profile.py:209 +#: judge/models/profile.py:215 msgid "User-defined JavaScript for site customization." msgstr "" -#: judge/models/profile.py:213 +#: judge/models/profile.py:219 msgid "current contest" msgstr "kỳ thi hiện tại" -#: judge/models/profile.py:220 +#: judge/models/profile.py:226 msgid "math engine" msgstr "" -#: judge/models/profile.py:224 +#: judge/models/profile.py:230 msgid "the rendering engine used to render math" msgstr "" -#: judge/models/profile.py:227 +#: judge/models/profile.py:233 msgid "2FA enabled" msgstr "" -#: judge/models/profile.py:229 +#: judge/models/profile.py:235 msgid "check to enable TOTP-based two factor authentication" msgstr "đánh dấu để sử dụng TOTP-based two factor authentication" -#: judge/models/profile.py:235 +#: judge/models/profile.py:241 msgid "TOTP key" msgstr "mã TOTP" -#: judge/models/profile.py:236 +#: judge/models/profile.py:242 msgid "32 character base32-encoded key for TOTP" msgstr "" -#: judge/models/profile.py:238 +#: judge/models/profile.py:244 msgid "TOTP key must be empty or base32" msgstr "" -#: judge/models/profile.py:242 +#: judge/models/profile.py:248 msgid "internal notes" msgstr "ghi chú nội bộ" -#: judge/models/profile.py:245 +#: judge/models/profile.py:251 msgid "Notes for administrators regarding this user." msgstr "Ghi chú riêng cho quản trị viên." -#: judge/models/profile.py:250 +#: judge/models/profile.py:256 msgid "Custom background" msgstr "Background tự chọn" -#: judge/models/profile.py:253 +#: judge/models/profile.py:259 msgid "CSS custom background properties: url(\"image_url\"), color, etc" msgstr "CSS background tự chọn. Ví dụ: url(\"image_url\"), white, ..." -#: judge/models/profile.py:383 +#: judge/models/profile.py:426 msgid "user profile" msgstr "thông tin người dùng" -#: judge/models/profile.py:384 +#: judge/models/profile.py:427 msgid "user profiles" msgstr "thông tin người dùng" -#: judge/models/profile.py:400 +#: judge/models/profile.py:443 msgid "request time" msgstr "thời gian đăng ký" -#: judge/models/profile.py:403 +#: judge/models/profile.py:446 msgid "state" msgstr "trạng thái" -#: judge/models/profile.py:410 +#: judge/models/profile.py:453 msgid "reason" msgstr "lý do" -#: judge/models/profile.py:413 +#: judge/models/profile.py:456 msgid "organization join request" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:414 +#: judge/models/profile.py:457 msgid "organization join requests" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:480 +#: judge/models/profile.py:523 #, fuzzy #| msgid "last seen" msgid "last visit" @@ -2401,15 +2419,15 @@ msgstr "Lỗi máy chấm" msgid "submission time" msgstr "thời gian bài nộp" -#: judge/models/submission.py:69 judge/models/submission.py:268 +#: judge/models/submission.py:69 judge/models/submission.py:308 msgid "execution time" msgstr "thời gian chạy" -#: judge/models/submission.py:70 judge/models/submission.py:269 +#: judge/models/submission.py:70 judge/models/submission.py:309 msgid "memory usage" msgstr "bộ nhớ sử dụng" -#: judge/models/submission.py:72 judge/models/submission.py:270 +#: judge/models/submission.py:72 judge/models/submission.py:310 msgid "points granted" msgstr "điểm" @@ -2457,47 +2475,47 @@ msgstr "được chấm lại bởi admin" msgid "was ran on pretests only" msgstr "chỉ chấm pretest" -#: judge/models/submission.py:234 templates/contest/moss.html:56 +#: judge/models/submission.py:274 templates/contest/moss.html:56 msgid "submissions" msgstr "bài nộp" -#: judge/models/submission.py:246 judge/models/submission.py:260 +#: judge/models/submission.py:286 judge/models/submission.py:300 msgid "associated submission" msgstr "bài nộp tương ứng" -#: judge/models/submission.py:264 +#: judge/models/submission.py:304 msgid "test case ID" msgstr "test case ID" -#: judge/models/submission.py:266 +#: judge/models/submission.py:306 msgid "status flag" msgstr "" -#: judge/models/submission.py:271 +#: judge/models/submission.py:311 msgid "points possible" msgstr "" -#: judge/models/submission.py:272 +#: judge/models/submission.py:312 msgid "batch number" msgstr "số thứ tự của nhóm" -#: judge/models/submission.py:274 +#: judge/models/submission.py:314 msgid "judging feedback" msgstr "phản hồi từ máy chấm" -#: judge/models/submission.py:277 +#: judge/models/submission.py:317 msgid "extended judging feedback" msgstr "phản hồi thêm từ máy chấm" -#: judge/models/submission.py:279 +#: judge/models/submission.py:319 msgid "program output" msgstr "output chương trình" -#: judge/models/submission.py:287 +#: judge/models/submission.py:327 msgid "submission test case" msgstr "cái testcase trong bài nộp" -#: judge/models/submission.py:288 +#: judge/models/submission.py:328 msgid "submission test cases" msgstr "cái testcase trong bài nộp" @@ -2603,59 +2621,59 @@ msgstr "Chỉnh sửa bài nộp" msgid "Recalculating user points" msgstr "Tính lại điểm người dùng" -#: judge/utils/problem_data.py:72 +#: judge/utils/problem_data.py:73 msgid "Empty batches not allowed." msgstr "Nhóm test trống là không hợp lệ." -#: judge/utils/problem_data.py:80 judge/utils/problem_data.py:88 -#: judge/utils/problem_data.py:103 +#: judge/utils/problem_data.py:81 judge/utils/problem_data.py:89 +#: judge/utils/problem_data.py:104 msgid "How did you corrupt the custom checker path?" msgstr "How did you corrupt the custom checker path?" -#: judge/utils/problem_data.py:131 +#: judge/utils/problem_data.py:132 #, python-format msgid "Points must be defined for non-batch case #%d." msgstr "Ô điểm số cho test #%d phải được điền." -#: judge/utils/problem_data.py:138 +#: judge/utils/problem_data.py:139 #, python-format msgid "Input file for case %d does not exist: %s" msgstr "File input cho test %d không tồn tại: %s" -#: judge/utils/problem_data.py:143 +#: judge/utils/problem_data.py:144 #, python-format msgid "Output file for case %d does not exist: %s" msgstr "File output cho test %d không tồn tại: %s" -#: judge/utils/problem_data.py:170 +#: judge/utils/problem_data.py:171 #, python-format msgid "Batch start case #%d requires points." msgstr "Nhóm test #%d cần được điền điểm số." -#: judge/utils/problem_data.py:193 +#: judge/utils/problem_data.py:194 #, python-format msgid "Attempt to end batch outside of one in case #%d" msgstr "Nhóm test #%d kết thúc không hợp lệ" -#: judge/utils/problem_data.py:212 +#: judge/utils/problem_data.py:213 msgid "How did you corrupt the zip path?" msgstr "" -#: judge/utils/problem_data.py:218 +#: judge/utils/problem_data.py:219 msgid "How did you corrupt the generator path?" msgstr "" -#: judge/utils/problem_data.py:236 +#: judge/utils/problem_data.py:237 msgid "Invalid interactor judge" msgstr "" -#: judge/utils/problem_data.py:260 +#: judge/utils/problem_data.py:261 #, fuzzy #| msgid "Invalid Return" msgid "Invalid signature handler" msgstr "Invalid Return" -#: judge/utils/problem_data.py:263 +#: judge/utils/problem_data.py:264 msgid "Invalid signature header" msgstr "" @@ -2735,165 +2753,171 @@ msgstr "Báo cáo" msgid "Comment feed" msgstr "Bình luận" -#: judge/views/comment.py:53 judge/views/pagevote.py:31 +#: judge/views/comment.py:47 judge/views/pagevote.py:31 msgid "Messing around, are we?" msgstr "Messing around, are we?" -#: judge/views/comment.py:69 judge/views/pagevote.py:47 +#: judge/views/comment.py:63 judge/views/pagevote.py:47 msgid "You must solve at least one problem before you can vote." msgstr "Bạn phải giải ít nhất 1 bài trước khi được vote." -#: judge/views/comment.py:100 +#: judge/views/comment.py:94 msgid "You already voted." msgstr "Bạn đã vote." -#: judge/views/comment.py:253 judge/views/organization.py:807 -#: judge/views/organization.py:957 judge/views/organization.py:1122 +#: judge/views/comment.py:246 judge/views/organization.py:808 +#: judge/views/organization.py:958 judge/views/organization.py:1120 msgid "Edited from site" msgstr "Chỉnh sửa từ web" -#: judge/views/comment.py:274 +#: judge/views/comment.py:267 msgid "Editing comment" msgstr "Chỉnh sửa bình luận" -#: judge/views/contests.py:120 judge/views/contests.py:370 -#: judge/views/contests.py:375 judge/views/contests.py:646 +#: judge/views/contests.py:123 judge/views/contests.py:386 +#: judge/views/contests.py:391 judge/views/contests.py:685 msgid "No such contest" msgstr "Không có contest nào như vậy" -#: judge/views/contests.py:121 judge/views/contests.py:371 +#: judge/views/contests.py:124 judge/views/contests.py:387 #, 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\"." -#: judge/views/contests.py:140 judge/views/stats.py:178 -#: templates/contest/list.html:246 templates/contest/list.html:291 -#: templates/contest/list.html:336 templates/contest/list.html:378 +#: judge/views/contests.py:143 judge/views/stats.py:178 +#: templates/contest/list.html:244 templates/contest/list.html:289 +#: templates/contest/list.html:334 templates/contest/list.html:376 #: templates/organization/org-left-sidebar.html:5 templates/stats/site.html:21 #: templates/user/user-bookmarks.html:56 msgid "Contests" msgstr "Kỳ thi" -#: judge/views/contests.py:375 +#: judge/views/contests.py:391 msgid "Could not find such contest." msgstr "Không tìm thấy kỳ thi nào như vậy." -#: judge/views/contests.py:383 +#: judge/views/contests.py:399 #, 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:434 +#: judge/views/contests.py:468 msgid "Clone Contest" msgstr "Nhân bản kỳ thi" -#: judge/views/contests.py:520 +#: judge/views/contests.py:559 msgid "Contest not ongoing" msgstr "Kỳ thi đang không diễn ra" -#: judge/views/contests.py:521 +#: judge/views/contests.py:560 #, python-format msgid "\"%s\" is not currently ongoing." msgstr "\"%s\" kỳ thi đang không diễn ra." -#: judge/views/contests.py:528 +#: judge/views/contests.py:567 msgid "Already in contest" msgstr "Đã ở trong kỳ thi" -#: judge/views/contests.py:529 +#: judge/views/contests.py:568 #, python-format msgid "You are already in a contest: \"%s\"." msgstr "Bạn đã ở trong kỳ thi: \"%s\"." -#: judge/views/contests.py:539 +#: judge/views/contests.py:578 msgid "Banned from joining" msgstr "Bị cấm tham gia" -#: judge/views/contests.py:541 +#: judge/views/contests.py:580 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:630 +#: judge/views/contests.py:669 #, python-format msgid "Enter access code for \"%s\"" msgstr "Nhập mật khẩu truy cập cho \"%s\"" -#: judge/views/contests.py:647 +#: judge/views/contests.py:686 #, python-format msgid "You are not in contest \"%s\"." msgstr "Bạn không ở trong kỳ thi \"%s\"." -#: judge/views/contests.py:670 +#: judge/views/contests.py:709 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:728 +#: judge/views/contests.py:767 #, python-format msgid "Contests in %(month)s" msgstr "Các kỳ thi trong %(month)s" -#: judge/views/contests.py:729 +#: judge/views/contests.py:768 msgid "F Y" msgstr "F Y" -#: judge/views/contests.py:789 +#: judge/views/contests.py:828 #, python-format msgid "%s Statistics" msgstr "%s Thống kê" -#: judge/views/contests.py:1085 +#: judge/views/contests.py:1124 #, python-format msgid "%s Rankings" msgstr "%s Bảng điểm" -#: judge/views/contests.py:1096 +#: judge/views/contests.py:1135 msgid "???" msgstr "???" -#: judge/views/contests.py:1123 +#: judge/views/contests.py:1162 #, python-format msgid "Your participation in %s" msgstr "Lần tham gia trong %s" -#: judge/views/contests.py:1124 +#: judge/views/contests.py:1163 #, python-format msgid "%s's participation in %s" msgstr "Lần tham gia của %s trong %s" -#: judge/views/contests.py:1138 +#: judge/views/contests.py:1177 msgid "Live" msgstr "Trực tiếp" -#: judge/views/contests.py:1157 templates/contest/contest-tabs.html:19 +#: judge/views/contests.py:1196 templates/contest/contest-tabs.html:21 msgid "Participation" msgstr "Lần tham gia" -#: judge/views/contests.py:1206 +#: judge/views/contests.py:1245 #, python-format msgid "%s MOSS Results" msgstr "%s Kết quả MOSS" -#: judge/views/contests.py:1242 +#: judge/views/contests.py:1281 #, python-format msgid "Running MOSS for %s..." msgstr "Đang chạy MOSS cho %s..." -#: judge/views/contests.py:1265 +#: judge/views/contests.py:1304 #, python-format msgid "Contest tag: %s" msgstr "Nhãn kỳ thi: %s" -#: judge/views/contests.py:1280 judge/views/ticket.py:72 +#: judge/views/contests.py:1319 judge/views/ticket.py:67 msgid "Issue description" msgstr "Mô tả vấn đề" -#: judge/views/contests.py:1323 +#: judge/views/contests.py:1362 #, python-format msgid "New clarification for %s" msgstr "Thông báo mới cho %s" +#: judge/views/contests.py:1468 +#, fuzzy +#| msgid "contest summary" +msgid "Contests Summary" +msgstr "tổng kết kỳ thi" + #: judge/views/email.py:21 msgid "New Email" msgstr "Email mới" @@ -2974,103 +2998,107 @@ msgstr "thời gian đăng ký" msgid "Runtimes" msgstr "Runtimes" -#: judge/views/notification.py:43 +#: judge/views/markdown_editor.py:12 +msgid "Markdown Editor" +msgstr "" + +#: judge/views/notification.py:29 #, python-format msgid "Notifications (%d unseen)" msgstr "Thông báo (%d chưa xem)" -#: judge/views/organization.py:148 judge/views/organization.py:155 +#: judge/views/organization.py:149 judge/views/organization.py:156 msgid "No such organization" msgstr "Không có tổ chức như vậy" -#: judge/views/organization.py:149 +#: judge/views/organization.py:150 #, python-format msgid "Could not find an organization with the key \"%s\"." msgstr "Không tìm thấy tổ chức với mã \"%s\"." -#: judge/views/organization.py:156 +#: judge/views/organization.py:157 msgid "Could not find such organization." msgstr "" -#: judge/views/organization.py:180 +#: judge/views/organization.py:181 msgid "Can't edit organization" msgstr "Không thể chỉnh sửa tổ chức" -#: judge/views/organization.py:181 +#: judge/views/organization.py:182 msgid "You are not allowed to edit this organization." msgstr "Bạn không được phép chỉnh sửa tổ chức này." -#: judge/views/organization.py:193 judge/views/organization.py:337 +#: judge/views/organization.py:194 judge/views/organization.py:338 msgid "Can't access organization" msgstr "Không thể truy cập nhóm" -#: judge/views/organization.py:194 judge/views/organization.py:338 +#: judge/views/organization.py:195 judge/views/organization.py:339 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:230 judge/views/stats.py:184 -#: templates/contest/list.html:93 templates/problem/list-base.html:97 +#: judge/views/organization.py:231 judge/views/stats.py:184 +#: templates/contest/list.html:93 templates/problem/list-base.html:91 #: templates/stats/site.html:33 templates/user/user-left-sidebar.html:4 #: templates/user/user-list-tabs.html:6 msgid "Groups" msgstr "Nhóm" -#: judge/views/organization.py:344 +#: judge/views/organization.py:345 #, python-format msgid "%s Members" msgstr "%s Thành viên" -#: judge/views/organization.py:466 +#: judge/views/organization.py:467 #, python-brace-format msgid "All submissions in {0}" msgstr "Bài nộp trong {0}" -#: judge/views/organization.py:496 judge/views/organization.py:502 -#: judge/views/organization.py:509 +#: judge/views/organization.py:497 judge/views/organization.py:503 +#: judge/views/organization.py:510 msgid "Joining group" msgstr "Tham gia nhóm" -#: judge/views/organization.py:497 +#: judge/views/organization.py:498 msgid "You are already in the group." msgstr "Bạn đã ở trong nhóm." -#: judge/views/organization.py:502 +#: judge/views/organization.py:503 msgid "This group is not open." msgstr "Nhóm này là nhóm kín." -#: judge/views/organization.py:510 +#: judge/views/organization.py:511 #, 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/views/organization.py:525 +#: judge/views/organization.py:526 msgid "Leaving group" msgstr "Rời nhóm" -#: judge/views/organization.py:526 +#: judge/views/organization.py:527 #, python-format msgid "You are not in \"%s\"." msgstr "Bạn không ở trong \"%s\"." -#: judge/views/organization.py:551 +#: judge/views/organization.py:552 #, python-format msgid "Request to join %s" msgstr "Đăng ký tham gia %s" -#: judge/views/organization.py:581 +#: judge/views/organization.py:582 msgid "Join request detail" msgstr "Chi tiết đơn đăng ký" -#: judge/views/organization.py:623 +#: judge/views/organization.py:624 msgid "Manage join requests" msgstr "Quản lý đơn đăng ký" -#: judge/views/organization.py:627 +#: judge/views/organization.py:628 #, python-format msgid "Managing join requests for %s" msgstr "Quản lý đơn đăng ký cho %s" -#: judge/views/organization.py:667 +#: judge/views/organization.py:668 #, python-format msgid "" "Your organization can only receive %d more members. You cannot approve %d " @@ -3079,96 +3107,105 @@ msgstr "" "Tổ chức chỉ có thể chứa %d thành viên. Bạn không thể chấp thuận nhiều hơn %d " "người." -#: judge/views/organization.py:685 +#: judge/views/organization.py:686 #, python-format msgid "Approved %d user." msgid_plural "Approved %d users." msgstr[0] "Đã chấp thuận %d người." -#: judge/views/organization.py:688 +#: judge/views/organization.py:689 #, python-format msgid "Rejected %d user." msgid_plural "Rejected %d users." msgstr[0] "Đã từ chối %d người." -#: judge/views/organization.py:728 +#: judge/views/organization.py:729 #, python-format msgid "Add member for %s" msgstr "Thêm thành viên cho %s" -#: judge/views/organization.py:740 +#: judge/views/organization.py:741 #, fuzzy #| msgid "Edited from site" msgid "Added members from site" msgstr "Chỉnh sửa từ web" -#: judge/views/organization.py:760 judge/views/organization.py:768 +#: judge/views/organization.py:761 judge/views/organization.py:769 msgid "Can't kick user" msgstr "Không thể đuổi" -#: judge/views/organization.py:761 +#: judge/views/organization.py:762 msgid "The user you are trying to kick does not exist!" msgstr "" -#: judge/views/organization.py:769 +#: judge/views/organization.py:770 #, python-format msgid "The user you are trying to kick is not in organization: %s." msgstr "" -#: judge/views/organization.py:790 judge/views/organization.py:946 -#, fuzzy, python-format -#| msgid "Editing %s" +#: judge/views/organization.py:791 judge/views/organization.py:947 +#, python-format msgid "Edit %s" -msgstr "Đang chỉnh sửa %s" +msgstr "Chỉnh sửa %s" -#: judge/views/organization.py:818 templates/organization/list.html:45 +#: judge/views/organization.py:819 templates/organization/list.html:45 msgid "Create group" msgstr "Tạo nhóm" -#: judge/views/organization.py:833 +#: judge/views/organization.py:834 msgid "Exceeded limit" msgstr "" -#: judge/views/organization.py:834 +#: judge/views/organization.py:835 #, python-format msgid "You created too many groups. You can only create at most %d groups" msgstr "" -#: judge/views/organization.py:839 judge/views/organization.py:864 -#: judge/views/organization.py:1012 +#: judge/views/organization.py:840 judge/views/organization.py:865 +#: judge/views/organization.py:1026 msgid "Added from site" msgstr "Thêm từ web" -#: judge/views/organization.py:855 +#: judge/views/organization.py:856 #: templates/organization/org-right-sidebar.html:52 msgid "Add contest" msgstr "Thêm kỳ thi" -#: judge/views/organization.py:898 judge/views/organization.py:1064 +#: judge/views/organization.py:899 judge/views/organization.py:1071 msgid "Permission denied" msgstr "Truy cập bị từ chối" -#: judge/views/organization.py:899 +#: judge/views/organization.py:900 #, fuzzy #| msgid "You are not allowed to edit this organization." msgid "You are not allowed to edit this contest" msgstr "Bạn không được phép chỉnh sửa tổ chức này." -#: judge/views/organization.py:1001 +#: judge/views/organization.py:951 templates/blog/blog.html:31 +#: templates/comments/content-list.html:59 +#: templates/comments/content-list.html:73 +#: templates/contest/contest-tabs.html:37 templates/contest/list.html:128 +#: templates/contest/tag-title.html:9 templates/flatpages/admin_link.html:3 +#: templates/license.html:10 templates/problem/editorial.html:15 +#: templates/problem/feed/problems.html:50 +msgid "Edit" +msgstr "Chỉnh sửa" + +#: judge/views/organization.py:1015 #, python-format msgid "Add blog for %s" msgstr "Thêm bài đăng cho %s" -#: judge/views/organization.py:1065 +#: judge/views/organization.py:1072 msgid "Not allowed to edit this blog" msgstr "Bạn không được phép chỉnh sửa bài đăng này." -#: judge/views/organization.py:1097 +#: judge/views/organization.py:1104 #, python-format msgid "Edit blog %s" msgstr "Chỉnh sửa %s" -#: judge/views/organization.py:1148 +#: judge/views/organization.py:1146 #, python-format msgid "Pending blogs in %s" msgstr "Bài đang đợi duyệt trong %s" @@ -3192,77 +3229,77 @@ msgstr "Hướng dẫn cho {0}" msgid "Editorial for {0}" msgstr "Hướng dẫn cho {0}" -#: judge/views/problem.py:461 templates/contest/contest.html:96 +#: judge/views/problem.py:461 templates/contest/contest.html:101 #: templates/organization/org-left-sidebar.html:4 #: templates/user/user-about.html:28 templates/user/user-bookmarks.html:35 #: templates/user/user-tabs.html:5 templates/user/users-table.html:19 msgid "Problems" msgstr "Bài tập" -#: judge/views/problem.py:837 +#: judge/views/problem.py:834 msgid "Problem feed" msgstr "Bài tập" -#: judge/views/problem.py:1062 +#: judge/views/problem.py:1057 msgid "Banned from submitting" msgstr "Bị cấm nộp bài" -#: judge/views/problem.py:1064 +#: judge/views/problem.py:1059 msgid "" "You have been declared persona non grata for this problem. You are " "permanently barred from submitting this problem." msgstr "Bạn đã bị cấm nộp bài này." -#: judge/views/problem.py:1087 +#: judge/views/problem.py:1082 msgid "Too many submissions" msgstr "Quá nhiều lần nộp" -#: judge/views/problem.py:1089 +#: judge/views/problem.py:1084 msgid "You have exceeded the submission limit for this problem." msgstr "Bạn đã vượt quá số lần nộp cho bài này." -#: judge/views/problem.py:1168 judge/views/problem.py:1173 +#: judge/views/problem.py:1163 judge/views/problem.py:1168 #, python-format msgid "Submit to %(problem)s" msgstr "Nộp bài cho %(problem)s" -#: judge/views/problem.py:1199 +#: judge/views/problem.py:1194 msgid "Clone Problem" msgstr "Nhân bản bài tập" -#: judge/views/problem_data.py:70 +#: judge/views/problem_data.py:71 msgid "Checker arguments must be a JSON object" msgstr "" -#: judge/views/problem_data.py:72 +#: judge/views/problem_data.py:73 msgid "Checker arguments is invalid JSON" msgstr "" -#: judge/views/problem_data.py:79 +#: judge/views/problem_data.py:80 msgid "Your zip file is invalid!" msgstr "File Zip không hợp lệ!" -#: judge/views/problem_data.py:169 +#: judge/views/problem_data.py:170 #, python-brace-format msgid "Comparing submissions for {0}" msgstr "So sánh các bài nộp cho {0}" -#: judge/views/problem_data.py:173 +#: judge/views/problem_data.py:174 #, python-brace-format msgid "Comparing submissions for {0}" msgstr "So sánh các bài nộp cho {0}" -#: judge/views/problem_data.py:210 +#: judge/views/problem_data.py:211 #, python-brace-format msgid "Editing data for {0}" msgstr "Chỉnh sửa dữ liệu cho {0}" -#: judge/views/problem_data.py:214 +#: judge/views/problem_data.py:215 #, python-format msgid "Editing data for %s" msgstr "Chỉnh sửa dữ liệu cho %s" -#: judge/views/problem_data.py:344 judge/views/problem_data.py:346 +#: judge/views/problem_data.py:348 judge/views/problem_data.py:350 #, python-format msgid "Generated init.yml for %s" msgstr "File init.yml cho %s" @@ -3330,7 +3367,7 @@ msgstr "Đăng ký" msgid "Authentication failure" msgstr "Xác thực thất bại" -#: judge/views/resolver.py:11 templates/contest/contest-tabs.html:26 +#: judge/views/resolver.py:11 templates/contest/contest-tabs.html:28 #, fuzzy #| msgid "solve rate" msgid "Resolver" @@ -3370,60 +3407,60 @@ msgstr "Kết quả chấm" msgid "Version matrix" msgstr "Ma trận phiên bản" -#: judge/views/submission.py:118 judge/views/submission.py:126 +#: judge/views/submission.py:93 judge/views/submission.py:101 #, python-format msgid "Submission of %(problem)s by %(user)s" msgstr "Bài nộp của %(user)s cho bài %(problem)s" -#: judge/views/submission.py:318 judge/views/submission.py:319 +#: judge/views/submission.py:293 judge/views/submission.py:294 #: templates/problem/problem.html:168 msgid "All submissions" msgstr "Tất cả bài nộp" -#: judge/views/submission.py:595 judge/views/submission.py:600 +#: judge/views/submission.py:560 judge/views/submission.py:565 msgid "All my submissions" msgstr "Tất cả bài nộp của tôi" -#: judge/views/submission.py:596 +#: judge/views/submission.py:561 #, python-format msgid "All submissions by %s" msgstr "Tất cả bài nộp của %s" -#: judge/views/submission.py:602 +#: judge/views/submission.py:567 #, python-brace-format msgid "All submissions by {0}" msgstr "Tất cả bài nộp của {0}" -#: judge/views/submission.py:623 +#: judge/views/submission.py:588 #, fuzzy #| msgid "All submissions" msgid "All friend submissions" msgstr "Tất cả bài nộp" -#: judge/views/submission.py:652 +#: judge/views/submission.py:617 #, python-format msgid "All submissions for %s" msgstr "Tất cả bài nộp cho %s" -#: judge/views/submission.py:680 +#: judge/views/submission.py:645 msgid "Must pass a problem" msgstr "Phải làm được một bài" -#: judge/views/submission.py:738 +#: judge/views/submission.py:703 #, python-format msgid "My submissions for %(problem)s" msgstr "Bài nộp của tôi cho %(problem)s" -#: judge/views/submission.py:739 +#: judge/views/submission.py:704 #, 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:884 +#: judge/views/submission.py:840 msgid "Must pass a contest" msgstr "Phải qua một kỳ thi" -#: judge/views/submission.py:914 +#: judge/views/submission.py:870 #, python-brace-format msgid "" "{0}'s submissions for {2} in {0} cho {2} trong {4}" -#: judge/views/submission.py:926 +#: judge/views/submission.py:882 #, python-brace-format msgid "" "{0}'s submissions for problem {2} in {3}" @@ -3441,50 +3478,50 @@ msgstr "" "Các bài nộp của {0} cho bài {2} trong {3}" "" -#: judge/views/submission.py:1063 +#: judge/views/submission.py:1016 #, fuzzy #| msgid "You do not have the permission to rejudge submissions." msgid "You don't have permission to access." msgstr "Bạn không có quyền chấm lại bài." -#: judge/views/ticket.py:65 judge/views/ticket.py:71 +#: judge/views/ticket.py:60 judge/views/ticket.py:66 msgid "Ticket title" msgstr "Tiêu đề báo cáo" -#: judge/views/ticket.py:129 judge/views/ticket.py:133 +#: judge/views/ticket.py:124 judge/views/ticket.py:128 #, python-format msgid "New ticket for %s" msgstr "Báo cáo mới cho %s" -#: judge/views/ticket.py:210 +#: judge/views/ticket.py:205 #, python-format msgid "%(title)s - Ticket %(id)d" msgstr "%(title)s - Báo cáo %(id)d" -#: judge/views/ticket.py:339 +#: judge/views/ticket.py:334 #, python-format msgid "Tickets - Page %(number)d of %(total)d" msgstr "Báo cáo - Trang %(number)d trên %(total)d" -#: judge/views/ticket.py:405 +#: judge/views/ticket.py:400 #, python-format msgid "New Ticket: %s" msgstr "Báo cáo mới: %s" -#: judge/views/ticket.py:408 +#: judge/views/ticket.py:403 #, python-format msgid "#%(id)d, assigned to: %(users)s" msgstr "#%(id)d, được phân cho: %(users)s" -#: judge/views/ticket.py:412 +#: judge/views/ticket.py:407 msgid ", " msgstr ", " -#: judge/views/ticket.py:417 +#: judge/views/ticket.py:412 msgid "no one" msgstr "không người nào" -#: judge/views/ticket.py:444 +#: judge/views/ticket.py:439 #, python-format msgid "New Ticket Message For: %s" msgstr "Tin nhắn báo cáo mới cho: %s" @@ -3532,7 +3569,7 @@ msgid "Updated on site" msgstr "Được cập nhật trên web" #: judge/views/user.py:431 templates/admin/auth/user/change_form.html:14 -#: templates/admin/auth/user/change_form.html:17 templates/base.html:300 +#: templates/admin/auth/user/change_form.html:17 templates/base.html:199 #: templates/user/user-tabs.html:11 msgid "Edit profile" msgstr "Chỉnh sửa thông tin" @@ -3575,6 +3612,10 @@ msgstr "Chia sẻ" msgid "Report" msgstr "Báo cáo" +#: templates/actionbar/media-js.html:116 +msgid "Copied link" +msgstr "Đã sao chép link" + #: templates/admin/judge/contest/change_form.html:9 msgid "Are you sure you want to rejudge ALL the submissions?" msgstr "Bạn có muốn chấm lại tất cả bài nộp?" @@ -3623,72 +3664,56 @@ msgstr "Chỉnh sửa thông tin" msgid "Rejudge" msgstr "Chấm lại" -#: templates/base.html:236 templates/chat/chat.html:65 +#: templates/base.html:137 msgid "Chat" msgstr "Chat" -#: templates/base.html:246 +#: templates/base.html:147 msgid "Notification" msgstr "Thông báo" -#: templates/base.html:267 +#: templates/base.html:173 msgid "Dark Mode" msgstr "" -#: templates/base.html:284 +#: templates/base.html:183 msgid "Profile" msgstr "Trang cá nhân" -#: templates/base.html:288 templates/chat/chat_js.html:5 -#: templates/comments/content-list.html:81 -#: templates/contest/contest-list-tabs.html:4 -#: templates/contest/ranking-table.html:47 templates/internal/problem.html:62 -#: templates/organization/org-left-sidebar.html:12 -#: templates/problem/left-sidebar.html:6 -#: templates/problem/problem-list-tabs.html:6 -#: templates/submission/info-base.html:12 templates/submission/list.html:395 -#: templates/submission/submission-list-tabs.html:15 -msgid "Admin" -msgstr "Admin" - -#: templates/base.html:293 +#: templates/base.html:192 msgid "Internal" msgstr "Nội bộ" -#: templates/base.html:296 +#: templates/base.html:195 msgid "Stats" msgstr "Thống kê" -#: templates/base.html:309 +#: templates/base.html:208 msgid "Log out" msgstr "Đăng xuất" -#: templates/base.html:319 +#: templates/base.html:218 #: templates/registration/password_reset_complete.html:4 msgid "Log in" msgstr "Đăng nhập" -#: templates/base.html:320 templates/registration/registration_form.html:72 -msgid "or" -msgstr "hoặc" - -#: templates/base.html:321 +#: templates/base.html:219 msgid "Sign up" msgstr "Đăng ký" -#: templates/base.html:335 +#: templates/base.html:233 msgid "spectating" msgstr "đang theo dõi" -#: templates/base.html:347 +#: templates/base.html:245 msgid "Compete" msgstr "Thi" -#: templates/base.html:349 +#: templates/base.html:247 msgid "General" msgstr "Chung" -#: templates/base.html:356 +#: templates/base.html:254 msgid "This site works best with JavaScript enabled." msgstr "" @@ -3697,15 +3722,6 @@ msgstr "" msgid " posted on %(time)s" msgstr "đã đăng vào %(time)s" -#: templates/blog/blog.html:31 templates/comments/content-list.html:62 -#: templates/comments/content-list.html:76 -#: templates/contest/contest-tabs.html:35 templates/contest/list.html:129 -#: templates/contest/tag-title.html:9 templates/flatpages/admin_link.html:3 -#: templates/license.html:10 templates/problem/editorial.html:15 -#: templates/problem/feed/problems.html:50 -msgid "Edit" -msgstr "Chỉnh sửa" - #: templates/blog/blog.html:35 templates/contest/contest.html:88 msgid "Edit in" msgstr "Chỉnh sửa trong" @@ -3760,45 +3776,29 @@ msgstr "Thêm mới" msgid "No clarifications have been made at this time." msgstr "Không có thông báo nào." -#: templates/chat/chat.html:5 templates/chat/chat_js.html:548 +#: templates/chat/chat.html:5 templates/chat/chat_js.html:539 msgid "Chat Box" msgstr "Chat Box" -#: templates/chat/chat.html:67 -msgid "Online Users" -msgstr "Trực tuyến" - -#: templates/chat/chat.html:78 templates/chat/chat_js.html:510 +#: templates/chat/chat.html:72 templates/chat/chat_js.html:501 #: templates/user/base-users-js.html:10 #: templates/user/base-users-two-col.html:19 msgid "Search by handle..." msgstr "Tìm kiếm theo tên..." -#: templates/chat/chat.html:97 +#: templates/chat/chat.html:91 msgid "Enter your message" msgstr "Nhập tin nhắn" -#: templates/chat/chat.html:98 +#: templates/chat/chat.html:92 msgid "Emoji" msgstr "" -#: templates/chat/chat_js.html:3 -msgid "Recent" -msgstr "Gần đây" - -#: templates/chat/chat_js.html:4 -msgid "Following" -msgstr "Bạn bè" - -#: templates/chat/chat_js.html:6 -msgid "Other" -msgstr "Thành viên khác" - -#: templates/chat/chat_js.html:124 +#: templates/chat/chat_js.html:118 msgid "New message(s)" msgstr "Tin nhắn mới" -#: templates/chat/chat_js.html:401 +#: templates/chat/chat_js.html:412 msgid "Mute this user and delete all messages?" msgstr "Mute người dùng này và xóa tất cả tin nhắn chung?" @@ -3816,57 +3816,57 @@ msgid "You are connect now. Say something to start the conversation." msgstr "Các bạn đã kết nối. Nhắn gì đó để bắt đầu cuộc trò chuyện." #: templates/chat/online_status.html:6 -#: templates/chat/user_online_status.html:14 +#: templates/chat/user_online_status.html:21 msgid "Lobby" msgstr "Sảnh chung" -#: templates/chat/user_online_status.html:19 +#: templates/chat/user_online_status.html:26 #, python-brace-format msgid "Last online on {time}" msgstr "Trực tuyến vào {time}" -#: templates/chat/user_online_status.html:31 +#: templates/chat/user_online_status.html:38 msgid "Unignore" msgstr "Mở lại thông báo" -#: templates/chat/user_online_status.html:33 +#: templates/chat/user_online_status.html:40 msgid "Ignore" msgstr "Tắt thông báo" -#: templates/chat/user_online_status.html:40 +#: templates/chat/user_online_status.html:47 msgid "users are online" msgstr "người đang trực tuyến" -#: templates/comments/content-list.html:14 -#: templates/comments/content-list.html:15 -#: templates/comments/content-list.html:23 -#: templates/comments/content-list.html:24 +#: templates/comments/content-list.html:11 +#: templates/comments/content-list.html:12 +#: templates/comments/content-list.html:20 +#: templates/comments/content-list.html:21 msgid "Please login to vote" msgstr "Đăng nhập để vote" -#: templates/comments/content-list.html:44 +#: templates/comments/content-list.html:41 #, python-format msgid "edit %(edits)s" msgstr "chỉnh sửa %(edits)s" -#: templates/comments/content-list.html:46 templates/comments/media-js.html:97 +#: templates/comments/content-list.html:43 templates/comments/media-js.html:97 msgid "edited" msgstr "đã chỉnh sửa" -#: templates/comments/content-list.html:55 templates/notification/list.html:14 +#: templates/comments/content-list.html:52 templates/notification/list.html:11 msgid "Link" msgstr "Link" -#: templates/comments/content-list.html:66 -#: templates/comments/content-list.html:72 +#: templates/comments/content-list.html:63 +#: templates/comments/content-list.html:69 msgid "Reply" msgstr "Phản hồi" -#: templates/comments/content-list.html:79 +#: templates/comments/content-list.html:76 msgid "Hide" msgstr "Ẩn" -#: templates/comments/content-list.html:94 +#: templates/comments/content-list.html:91 #, python-format msgid "" "This comment is hidden due to too much negative feedback. Click đây để mở." -#: templates/comments/content-list.html:111 +#: templates/comments/content-list.html:108 msgid "reply" msgid_plural "replies" msgstr[0] "phản hồi" -#: templates/comments/content-list.html:136 +#: templates/comments/content-list.html:133 msgid "more comment" msgid_plural "more comments" msgstr[0] "bình luận nữa" @@ -3975,7 +3975,7 @@ msgstr "Thứ sáu" msgid "Saturday" msgstr "Thứ bảy" -#: templates/contest/clarification.html:52 templates/contest/list.html:232 +#: templates/contest/clarification.html:52 templates/contest/list.html:230 #: templates/organization/new.html:10 templates/ticket/new.html:38 msgid "Create" msgstr "Tạo mới" @@ -3984,12 +3984,6 @@ msgstr "Tạo mới" msgid "Enter a new key for the cloned contest:" msgstr "Nhập mã kỳ thi cho kỳ thi nhân bản:" -#: templates/contest/clone.html:47 -#, fuzzy -#| msgid "Groups" -msgid "Group:" -msgstr "Nhóm" - #: templates/contest/clone.html:55 templates/problem/clone.html:40 msgid "Clone!" msgstr "Nhân bản!" @@ -4069,39 +4063,35 @@ msgstr "Danh sách" msgid "Calendar" msgstr "Lịch" -#: templates/contest/contest-tabs.html:10 +#: templates/contest/contest-tabs.html:11 msgid "Info" msgstr "Thông tin" -#: templates/contest/contest-tabs.html:12 templates/submission/list.html:363 +#: templates/contest/contest-tabs.html:13 templates/submission/list.html:363 msgid "Statistics" msgstr "Thống kê" -#: templates/contest/contest-tabs.html:17 +#: templates/contest/contest-tabs.html:19 msgid "Rankings" msgstr "Bảng xếp hạng" -#: templates/contest/contest-tabs.html:22 +#: templates/contest/contest-tabs.html:24 msgid "Hidden Rankings" msgstr "Bảng xếp hạng ẩn" -#: templates/contest/contest-tabs.html:29 +#: templates/contest/contest-tabs.html:31 msgid "Final rankings" msgstr "BXH chung cuộc" -#: templates/contest/contest-tabs.html:33 +#: templates/contest/contest-tabs.html:35 msgid "MOSS" msgstr "MOSS" -#: templates/contest/contest-tabs.html:38 -msgid "Clone" -msgstr "Nhân bản" - #: templates/contest/contest.html:35 templates/contest/contest.html:55 msgid "Leave contest" msgstr "Rời kỳ thi" -#: templates/contest/contest.html:42 templates/contest/list.html:400 +#: templates/contest/contest.html:42 templates/contest/list.html:398 msgid "Virtual join" msgstr "Tham gia ảo" @@ -4121,21 +4111,36 @@ msgstr "Tham gia kỳ thi" msgid "Login to participate" msgstr "Đăng nhập để tham gia" -#: templates/contest/contest.html:102 +#: templates/contest/contest.html:92 +msgid "Clone" +msgstr "Nhân bản" + +#: templates/contest/contest.html:107 msgid "AC Rate" msgstr "Tỷ lệ AC" -#: templates/contest/contest.html:103 templates/contest/list.html:269 -#: templates/contest/list.html:310 templates/contest/list.html:392 +#: templates/contest/contest.html:108 templates/contest/list.html:267 +#: templates/contest/list.html:308 templates/contest/list.html:390 #: templates/problem/list.html:24 msgid "Users" msgstr "Người nộp" -#: templates/contest/contest.html:128 templates/problem/list.html:58 +#: templates/contest/contest.html:133 templates/problem/list.html:58 #: templates/problem/list.html:133 msgid "Editorial" msgstr "Hướng dẫn" +#: templates/contest/contests_summary.html:36 +#: templates/user/base-users-table.html:3 +msgid "Rank" +msgstr "Rank" + +#: templates/contest/contests_summary.html:37 +#: templates/status/language-list.html:34 +#: templates/user/import/table_csv.html:6 +msgid "Name" +msgstr "Tên" + #: templates/contest/list.html:89 templates/contest/media-js.html:152 msgid "Are you sure you want to join?" msgstr "Bạn có chắc tham gia?" @@ -4148,83 +4153,83 @@ msgstr "" "Tham gia kỳ thi lần đầu sẽ kích hoạt thời gian đếm ngược, không thể dừng lại " "sau đó." -#: templates/contest/list.html:123 +#: templates/contest/list.html:122 msgid "hidden" msgstr "ẩn" -#: templates/contest/list.html:135 +#: templates/contest/list.html:134 msgid "private" msgstr "riêng tư" -#: templates/contest/list.html:151 +#: templates/contest/list.html:150 msgid "rated" msgstr "rated" -#: templates/contest/list.html:177 +#: templates/contest/list.html:175 #, python-format msgid "%(time_limit)s window" msgstr "Cửa sổ thi dài %(time_limit)s" -#: templates/contest/list.html:179 +#: templates/contest/list.html:177 #, python-format msgid "%(duration)s long" msgstr "Kéo dài %(duration)s" -#: templates/contest/list.html:198 +#: templates/contest/list.html:196 msgid "Spectate" msgstr "Theo dõi" -#: templates/contest/list.html:204 templates/organization/home.html:30 +#: templates/contest/list.html:202 templates/organization/home.html:30 msgid "Join" msgstr "Tham gia" -#: templates/contest/list.html:214 +#: templates/contest/list.html:212 msgid "Search contests..." msgstr "Tìm kiếm kỳ thi..." -#: templates/contest/list.html:224 templates/internal/problem.html:33 +#: templates/contest/list.html:222 templates/internal/problem.html:33 msgid "Search" msgstr "Tìm kiếm" -#: templates/contest/list.html:228 +#: templates/contest/list.html:226 msgid "Hide organization contests" msgstr "Ẩn các kỳ thi riêng tư của nhóm" -#: templates/contest/list.html:238 +#: templates/contest/list.html:236 msgid "Active Contests" msgstr "Kỳ thi bạn đang tham gia" -#: templates/contest/list.html:255 +#: templates/contest/list.html:253 #, python-format msgid "Window ends in %(countdown)s" msgstr "Cửa số thi còn %(countdown)s" -#: templates/contest/list.html:258 templates/contest/list.html:299 +#: templates/contest/list.html:256 templates/contest/list.html:297 #, python-format msgid "Ends in %(countdown)s" msgstr "Kết thúc trong %(countdown)s" -#: templates/contest/list.html:284 +#: templates/contest/list.html:282 msgid "Ongoing Contests" msgstr "Kỳ thi đang diễn ra" -#: templates/contest/list.html:322 +#: templates/contest/list.html:320 msgid "There is no ongoing contest at this time." msgstr "Không có kỳ thi nào đang diễn ra hiện tại." -#: templates/contest/list.html:329 +#: templates/contest/list.html:327 msgid "Upcoming Contests" msgstr "Kỳ thi sắp tới" -#: templates/contest/list.html:359 +#: templates/contest/list.html:357 msgid "There is no scheduled contest at this time." msgstr "Không có kỳ thi nào được lên lịch hiện tại." -#: templates/contest/list.html:366 +#: templates/contest/list.html:364 msgid "Past Contests" msgstr "Kỳ thi trong quá khứ" -#: templates/contest/list.html:413 +#: templates/contest/list.html:411 msgid "There is no past contest." msgstr "Không có kỳ thi nào trong quá khứ." @@ -4395,6 +4400,7 @@ msgid "Upload file" msgstr "Tải file lên" #: templates/fine_uploader/script.html:23 +#: templates/markdown_editor/markdown_editor.html:131 msgid "Cancel" msgstr "Hủy" @@ -4458,14 +4464,23 @@ msgstr "Gợi ý" msgid "Source:" msgstr "Nguồn:" -#: templates/notification/list.html:7 -msgid "You have no notifications" -msgstr "Bạn không có thông báo" +#: templates/markdown_editor/markdown_editor.html:111 templates/pagedown.html:9 +msgid "Update Preview" +msgstr "Cập nhật xem trước" -#: templates/notification/list.html:13 -msgid "Activity" -msgstr "Hoạt động" +#: templates/markdown_editor/markdown_editor.html:115 +msgid "Insert Image" +msgstr "Chèn hình ảnh" +#: templates/markdown_editor/markdown_editor.html:118 +msgid "From the web" +msgstr "Từ web" + +#: templates/markdown_editor/markdown_editor.html:124 +msgid "From your computer" +msgstr "Từ máy tính của bạn" + +#: templates/markdown_editor/markdown_editor.html:130 #: templates/organization/blog/edit.html:36 #: templates/organization/contest/add.html:36 #: templates/organization/contest/edit.html:86 @@ -4473,12 +4488,20 @@ msgstr "Hoạt động" msgid "Save" msgstr "Lưu" +#: templates/notification/list.html:5 +msgid "You have no notifications" +msgstr "Bạn không có thông báo" + +#: templates/notification/list.html:10 +msgid "Activity" +msgstr "Hoạt động" + #: templates/organization/blog/pending.html:8 msgid "Blog" msgstr "" #: templates/organization/blog/pending.html:11 -#: templates/problem/search-form.html:57 +#: templates/problem/search-form.html:50 msgid "Author" msgstr "Tác giả" @@ -4632,10 +4655,6 @@ msgstr "Từ chối" msgid "Kick" msgstr "Đuổi" -#: templates/pagedown.html:9 -msgid "Update Preview" -msgstr "Cập nhật xem trước" - #: templates/problem/clone.html:37 msgid "Enter a new code for the cloned problem:" msgstr "Nhập mã bài mới cho bài tập được nhân bản:" @@ -4749,7 +4768,7 @@ msgid "Thinking point" msgstr "Độ khó nghĩ" #: templates/problem/feed/problems.html:83 -#: templates/problem/search-form.html:68 +#: templates/problem/search-form.html:61 msgid "Problem types" msgstr "Dạng bài" @@ -4761,23 +4780,23 @@ msgstr "Lưu ý thêm cho admin" msgid "Feed" msgstr "Gợi ý" -#: templates/problem/list-base.html:95 +#: templates/problem/list-base.html:89 msgid "Filter by type..." msgstr "Lọc theo dạng..." -#: templates/problem/list-base.html:158 templates/problem/list-base.html:184 +#: templates/problem/list-base.html:152 templates/problem/list-base.html:178 msgid "Add types..." msgstr "Thêm dạng" -#: templates/problem/list-base.html:200 +#: templates/problem/list-base.html:194 msgid "Fail to vote!" msgstr "Hệ thống lỗi!" -#: templates/problem/list-base.html:203 +#: templates/problem/list-base.html:197 msgid "Successful vote! Thank you!" msgstr "Đã gửi thành công! Cảm ơn bạn!" -#: templates/problem/list.html:40 templates/problem/search-form.html:79 +#: templates/problem/list.html:40 templates/problem/search-form.html:72 #: templates/user/user-problems.html:57 msgid "Category" msgstr "Nhóm bài" @@ -5041,26 +5060,22 @@ msgstr "Hiển thị dạng bài" msgid "Show editorial" msgstr "Hiển thị hướng dẫn" -#: templates/problem/search-form.html:41 -msgid "Have editorial" -msgstr "Có hướng dẫn" - -#: templates/problem/search-form.html:82 templates/problem/search-form.html:84 +#: templates/problem/search-form.html:75 templates/problem/search-form.html:77 #: templates/submission/list.html:381 #: templates/submission/submission-list-tabs.html:4 msgid "All" msgstr "Tất cả" -#: templates/problem/search-form.html:94 +#: templates/problem/search-form.html:87 msgid "Point range" msgstr "Mốc điểm" -#: templates/problem/search-form.html:100 templates/submission/list.html:356 +#: templates/problem/search-form.html:93 templates/submission/list.html:356 #: templates/ticket/list.html:248 msgid "Go" msgstr "Lọc" -#: templates/problem/search-form.html:101 +#: templates/problem/search-form.html:94 msgid "Random" msgstr "Ngẫu nhiên" @@ -5283,6 +5298,10 @@ msgstr "(một lần nữa)" msgid "(select your closest major city)" msgstr "(chọn thành phố gần nhất)" +#: templates/registration/registration_form.html:72 +msgid "or" +msgstr "hoặc" + #: templates/registration/registration_form.html:73 msgid "pick from map" msgstr "chọn từ bản đồ" @@ -5369,11 +5388,6 @@ msgstr "Không có máy chấm nào hoạt động." msgid "ID" msgstr "ID" -#: templates/status/language-list.html:34 -#: templates/user/import/table_csv.html:6 -msgid "Name" -msgstr "Tên" - #: templates/status/language-list.html:35 msgid "Runtime Info" msgstr "Thông tin Runtime" @@ -5636,7 +5650,7 @@ msgstr "Người được ủy thác" #: templates/ticket/new_problem.html:7 msgid "Thanks for opening a ticket!" -msgstr "Cảm ơn đã báo cáo!" +msgstr "Cảm ơn bạn đã báo cáo!" #: templates/ticket/new_problem.html:9 msgid "" @@ -5683,10 +5697,6 @@ msgstr "Top Rating" msgid "Top Score" msgstr "Top Score" -#: templates/user/base-users-table.html:3 -msgid "Rank" -msgstr "Rank" - #: templates/user/edit-profile.html:111 msgid "Change your password" msgstr "Đổi mật khẩu" @@ -5800,72 +5810,72 @@ msgstr "Rank theo điểm" msgid "Admin Notes" msgstr "Lưu ý của admin" -#: templates/user/user-about.html:79 +#: templates/user/user-about.html:77 msgid "You have not shared any information." msgstr "Bạn chưa chia sẻ thông tin nào." -#: templates/user/user-about.html:81 +#: templates/user/user-about.html:79 msgid "This user has not shared any information." msgstr "Người dùng này chưa chia sẻ thông tin nào." -#: templates/user/user-about.html:90 +#: templates/user/user-about.html:88 msgid "Awards" msgstr "Thành tích" -#: templates/user/user-about.html:101 +#: templates/user/user-about.html:99 #, python-format msgid "%(label)s (%(date)s)" msgstr "%(label)s (%(date)s)" -#: templates/user/user-about.html:119 +#: templates/user/user-about.html:117 msgid "Mon" msgstr "Thứ 2" -#: templates/user/user-about.html:124 +#: templates/user/user-about.html:122 msgid "Tues" msgstr "Thứ 3" -#: templates/user/user-about.html:129 +#: templates/user/user-about.html:127 msgid "Wed" msgstr "Thứ 4" -#: templates/user/user-about.html:134 +#: templates/user/user-about.html:132 msgid "Thurs" msgstr "Thứ 5" -#: templates/user/user-about.html:139 +#: templates/user/user-about.html:137 msgid "Fri" msgstr "Thứ 6" -#: templates/user/user-about.html:144 +#: templates/user/user-about.html:142 msgid "Sat" msgstr "Thứ 7" -#: templates/user/user-about.html:149 +#: templates/user/user-about.html:147 msgid "Sun" msgstr "CN" -#: templates/user/user-about.html:158 +#: templates/user/user-about.html:156 msgid "Less" msgstr "Ít" -#: templates/user/user-about.html:164 +#: templates/user/user-about.html:162 msgid "More" msgstr "Nhiều" -#: templates/user/user-about.html:173 +#: templates/user/user-about.html:171 msgid "Rating History" msgstr "Các lần thi" -#: templates/user/user-about.html:244 +#: templates/user/user-about.html:242 msgid "past year" msgstr "năm ngoái" -#: templates/user/user-about.html:261 +#: templates/user/user-about.html:259 msgid "total submission(s)" msgstr "bài nộp" -#: templates/user/user-about.html:265 +#: templates/user/user-about.html:263 msgid "submissions in the last year" msgstr "bài nộp trong năm qua" @@ -5951,6 +5961,21 @@ msgstr "Thông tin" msgid "Check all" msgstr "Chọn tất cả" +#~ msgid "Following" +#~ msgstr "Bạn bè" + +#~ msgid "Other" +#~ msgstr "Thành viên khác" + +#~ msgid "Online Users" +#~ msgstr "Trực tuyến" + +#~ msgid "Group:" +#~ msgstr "Nhóm:" + +#~ msgid "Have editorial" +#~ msgstr "Có hướng dẫn" + #~ msgid "Contest" #~ msgstr "Kỳ thi" @@ -6267,4 +6292,3 @@ msgstr "Chọn tất cả" #~ msgid "Hard" #~ msgstr "Khó" - diff --git a/locale/vi/LC_MESSAGES/dmoj-user.po b/locale/vi/LC_MESSAGES/dmoj-user.po index 4ea605c..d99d288 100644 --- a/locale/vi/LC_MESSAGES/dmoj-user.po +++ b/locale/vi/LC_MESSAGES/dmoj-user.po @@ -39,12 +39,6 @@ msgstr "Đăng ký tên" msgid "Report" msgstr "Báo cáo" -msgid "Insert Image" -msgstr "Chèn hình ảnh" - -msgid "Save" -msgstr "Lưu" - msgid "2sat" msgstr "" @@ -600,3 +594,8 @@ msgstr "" msgid "z-function" msgstr "" +#~ msgid "Insert Image" +#~ msgstr "Chèn hình ảnh" + +#~ msgid "Save" +#~ msgstr "Lưu" diff --git a/resources/blog.scss b/resources/blog.scss index 4c57cfe..265ebb9 100644 --- a/resources/blog.scss +++ b/resources/blog.scss @@ -147,8 +147,9 @@ } } -.blog-box:hover { +.blog-box:hover, .blog-box:not(.pre-expand-blog) { border-color: #8a8a8a; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.1); } .blog-description { diff --git a/resources/comments.scss b/resources/comments.scss index 05ef84f..dd2fe50 100644 --- a/resources/comments.scss +++ b/resources/comments.scss @@ -269,10 +269,10 @@ a { .actionbar-button { cursor: pointer; padding: 0.8em; - border: 0.2px solid lightgray; border-radius: 5em; font-weight: bold; display: inherit; + background: lightgray; } .actionbar-block { display: flex; @@ -287,7 +287,7 @@ a { border-radius: 5em 0 0 5em; } .actionbar-button:hover { - background: lightgray; + background: darkgray; } .dislike-button { padding-left: 0.5em; diff --git a/resources/content-description.scss b/resources/content-description.scss index 69ff279..442be32 100644 --- a/resources/content-description.scss +++ b/resources/content-description.scss @@ -42,7 +42,6 @@ white-space: pre-wrap; word-wrap: break-word; - margin: 1.5em 0 1.5em 0; padding: 1em; border: 1px solid $border_gray; background-color: #f8f8f8; diff --git a/resources/darkmode.css b/resources/darkmode.css index f9a4884..a50a400 100644 --- a/resources/darkmode.css +++ b/resources/darkmode.css @@ -1956,8 +1956,10 @@ input::placeholder { background-color: rgb(24, 26, 27); box-shadow: rgba(0, 0, 0, 0.2) 0px 0px 5px; } -.blog-box:hover { +.blog-box:hover, +.blog-box:not(.pre-expand-blog) { border-color: rgb(81, 88, 91); + box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 2px; } .problem-feed-name a { color: rgb(102, 177, 250); @@ -2091,6 +2093,9 @@ ul.problem-list { #comment-announcement:hover { background-color: rgb(96, 104, 108); } +.new-problem-info { + background-color: rgb(54, 39, 0); +} .admin a, .admin { color: rgb(232, 230, 227) !important; @@ -2780,11 +2785,12 @@ a.voted { border-left-color: rgb(48, 52, 54); } .actionbar .actionbar-button { - border-color: rgb(60, 65, 68); + background-image: initial; + background-color: rgb(49, 53, 55); } .actionbar .actionbar-button:hover { background-image: initial; - background-color: rgb(49, 53, 55); + background-color: rgb(73, 79, 82); } .actionbar .dislike-button { border-left-color: initial; diff --git a/resources/dmmd-preview.scss b/resources/dmmd-preview.scss index a3236b7..98cdbaa 100644 --- a/resources/dmmd-preview.scss +++ b/resources/dmmd-preview.scss @@ -25,17 +25,17 @@ div.dmmd-preview-has-content div.dmmd-preview-content { } div.dmmd-no-button div.dmmd-preview-update { - display: none; + display: none; } div.dmmd-no-button div.dmmd-preview-content { - padding-bottom: 0; + padding-bottom: 0; } div.dmmd-no-button:not(.dmmd-preview-has-content) { - display: none; + display: none; } div.dmmd-preview-stale { - background: repeating-linear-gradient(-45deg, #fff, #fff 10px, #f8f8f8 10px, #f8f8f8 20px); + background: repeating-linear-gradient(-45deg, #fff, #fff 10px, #f8f8f8 10px, #f8f8f8 20px); } diff --git a/resources/fine-uploader/templates/default.html b/resources/fine-uploader/templates/default.html index e3285ad..11c1502 100644 --- a/resources/fine-uploader/templates/default.html +++ b/resources/fine-uploader/templates/default.html @@ -4,59 +4,59 @@ if you are not yet familiar with Fine Uploader UI. --> diff --git a/resources/fine-uploader/templates/gallery.html b/resources/fine-uploader/templates/gallery.html index 3c7d69e..44827a6 100644 --- a/resources/fine-uploader/templates/gallery.html +++ b/resources/fine-uploader/templates/gallery.html @@ -5,78 +5,78 @@ on how to customize this template. --> diff --git a/resources/fine-uploader/templates/simple-thumbnails.html b/resources/fine-uploader/templates/simple-thumbnails.html index 668c0c0..767d2d1 100644 --- a/resources/fine-uploader/templates/simple-thumbnails.html +++ b/resources/fine-uploader/templates/simple-thumbnails.html @@ -5,60 +5,60 @@ on how to customize this template. --> diff --git a/templates/loading-page.html b/resources/html/loading-page.html similarity index 100% rename from templates/loading-page.html rename to resources/html/loading-page.html diff --git a/resources/math.scss b/resources/math.scss index f3dae2d..dac4680 100644 --- a/resources/math.scss +++ b/resources/math.scss @@ -1,49 +1,49 @@ .mwe-math-mathml-inline { - display: inline !important; + display: inline !important; } .mwe-math-mathml-display { - display: block !important; - margin-left: auto; - margin-right: auto; + display: block !important; + margin-left: auto; + margin-right: auto; } .mwe-math-mathml-a11y { - clip: rect(1px, 1px, 1px, 1px); - overflow: hidden; - position: absolute; - width: 1px; - height: 1px; - opacity: 0; + clip: rect(1px, 1px, 1px, 1px); + overflow: hidden; + position: absolute; + width: 1px; + height: 1px; + opacity: 0; } .mwe-math-fallback-image-inline { - display: inline-block; - vertical-align: middle; + display: inline-block; + vertical-align: middle; } .mwe-math-fallback-image-display { - display: block; - margin-left: auto !important; - margin-right: auto !important; + display: block; + margin-left: auto !important; + margin-right: auto !important; } @font-face { - font-family: 'Latin Modern Math'; - src: url('libs/latinmodernmath/latinmodern-math.eot'); /* IE9 Compat Modes */ - src: local('Latin Modern Math'), local('LatinModernMath-Regular'), + font-family: 'Latin Modern Math'; + src: url('libs/latinmodernmath/latinmodern-math.eot'); /* IE9 Compat Modes */ + src: local('Latin Modern Math'), local('LatinModernMath-Regular'), url('libs/latinmodernmath/latinmodern-math.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('libs/latinmodernmath/latinmodern-math.woff2') format('woff2'), /* Modern Browsers */ url('libs/latinmodernmath/latinmodern-math.woff') format('woff'), /* Modern Browsers */ url('libs/latinmodernmath/latinmodern-math.ttf') format('truetype'); /* Safari, Android, iOS */ - font-weight: normal; - font-style: normal; + font-weight: normal; + font-style: normal; } math { - font-family: "Latin Modern Math"; + font-family: "Latin Modern Math"; } img.inline-math { - display: inline; + display: inline; } \ No newline at end of file diff --git a/resources/problem.scss b/resources/problem.scss index ddb3690..ff2b8d1 100644 --- a/resources/problem.scss +++ b/resources/problem.scss @@ -416,3 +416,19 @@ ul.problem-list { } } } + +.new-problem-info { + background-color: #fff6dd; + border-radius: 25px; + font-size: 14px; + height: 25px; + width: 100%; + display: table; + padding: 5px; + margin-top: 12px; +} + +.info-block { + display:table-cell; + vertical-align:middle; +} \ No newline at end of file diff --git a/resources/users.scss b/resources/users.scss index 152ffab..841e7bd 100644 --- a/resources/users.scss +++ b/resources/users.scss @@ -50,7 +50,7 @@ th.header.rank { th a:hover { color: #0F0; } - + .about-column { width: 30%; } @@ -362,7 +362,7 @@ a.edit-profile { } .follow { - background: green; + background: green; border-color: lightgreen; } .follow:hover { diff --git a/templates/actionbar/media-js.html b/templates/actionbar/media-js.html index e4f281e..f1dec97 100644 --- a/templates/actionbar/media-js.html +++ b/templates/actionbar/media-js.html @@ -113,7 +113,7 @@ navigator.clipboard .writeText(link) .then(() => { - showTooltip(element, "Copied link", 'n'); + showTooltip(element, "{{_('Copied link')}}", 'n'); }); }; diff --git a/templates/base.html b/templates/base.html index e595387..c82368d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -75,112 +75,6 @@ {% endcompress %} {% endif %} - {% if not INLINE_JQUERY %} - - {% endif %} - - {% compress js %} - - {% if INLINE_JQUERY %} - - {% endif %} - - - - - - - {% include "extra_js.html" %} - - - - {% endcompress %} - - {% block js_media %}{% endblock %} - {% if request.in_contest %} - - {% endif %} - - {% if request.user.is_authenticated %} - - {% else %} - - {% endif %} - - {% if misc_config.analytics %} - {{ misc_config.analytics|safe }} - {% endif %} - - {# Don't run userscript since it may be malicious #} - {% if request.user.is_authenticated and request.profile.user_script and not request.user.is_impersonate %} - - {% endif %}