diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8740838..fc3bd4b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -11,7 +11,3 @@ repos:
rev: 22.12.0
hooks:
- id: black
- - repo: https://github.com/hadialqattan/pycln
- rev: 'v2.3.0'
- hooks:
- - id: pycln
diff --git a/502.html b/502.html
index a65c3cd..98f4dbb 100644
--- a/502.html
+++ b/502.html
@@ -49,7 +49,7 @@
" + escape(code) + "") - value = f"```{options}\n{code}\n```\n" - return mark_safe(markdown(value)) + +try: + import pygments + import pygments.lexers + import pygments.formatters + import pygments.util +except ImportError: + + def highlight_code(code, language, cssclass=None): + return _make_pre_code(code) + +else: + + def highlight_code(code, language, cssclass="codehilite", linenos=True): + try: + lexer = pygments.lexers.get_lexer_by_name(language) + except pygments.util.ClassNotFound: + return _make_pre_code(code) + + if linenos: + return mark_safe( + pygments.highlight( + code, + lexer, + pygments.formatters.HtmlFormatter( + cssclass=cssclass, linenos="table", wrapcode=True + ), + ) + ) + return mark_safe( + pygments.highlight( + code, + lexer, + pygments.formatters.HtmlFormatter(cssclass=cssclass, wrapcode=True), + ) + ) diff --git a/judge/jinja2/__init__.py b/judge/jinja2/__init__.py index e24ea8c..93d0ed5 100644 --- a/judge/jinja2/__init__.py +++ b/judge/jinja2/__init__.py @@ -21,8 +21,8 @@ from . import ( render, social, spaceless, + submission, timedelta, - comment, ) from . import registry diff --git a/judge/jinja2/comment.py b/judge/jinja2/comment.py deleted file mode 100644 index 6baa365..0000000 --- a/judge/jinja2/comment.py +++ /dev/null @@ -1,12 +0,0 @@ -from . import registry - -from django.contrib.contenttypes.models import ContentType - -from judge.models.comment import get_visible_comment_count -from judge.caching import cache_wrapper - - -@registry.function -def comment_count(obj): - content_type = ContentType.objects.get_for_model(obj) - return get_visible_comment_count(content_type, obj.pk) diff --git a/judge/jinja2/datetime.py b/judge/jinja2/datetime.py index ce7cf32..eb0ec41 100644 --- a/judge/jinja2/datetime.py +++ b/judge/jinja2/datetime.py @@ -23,5 +23,5 @@ registry.filter(localtime_wrapper(time)) @registry.function @registry.render_with("widgets/relative-time.html") -def relative_time(time, format=_("N j, Y, g:i a"), rel=_("{time}"), abs=_("{time}")): +def relative_time(time, format=_("N j, Y, g:i a"), rel=_("{time}"), abs=_("on {time}")): return {"time": time, "format": format, "rel_format": rel, "abs_format": abs} diff --git a/judge/jinja2/gravatar.py b/judge/jinja2/gravatar.py index 175992f..2848d92 100644 --- a/judge/jinja2/gravatar.py +++ b/judge/jinja2/gravatar.py @@ -9,16 +9,14 @@ from . import registry @registry.function -def gravatar(profile, size=80, default=None, profile_image=None, email=None): - if profile and not profile.is_muted: - if profile_image: - return profile_image - if profile and profile.profile_image_url: - return profile.profile_image_url - if profile: - email = email or profile.email +def gravatar(email, size=80, default=None): + if isinstance(email, Profile): if default is None: - default = profile.is_muted + default = email.mute + email = email.user.email + elif isinstance(email, AbstractUser): + email = email.email + gravatar_url = ( "//www.gravatar.com/avatar/" + hashlib.md5(utf8bytes(email.strip().lower())).hexdigest() diff --git a/judge/jinja2/markdown/__init__.py b/judge/jinja2/markdown/__init__.py index 18355d4..d54f103 100644 --- a/judge/jinja2/markdown/__init__.py +++ b/judge/jinja2/markdown/__init__.py @@ -1,7 +1,112 @@ from .. import registry -from judge.markdown import markdown as _markdown +import markdown as _markdown +import bleach +from django.utils.html import escape +from bs4 import BeautifulSoup +from pymdownx import superfences + + +EXTENSIONS = [ + "pymdownx.arithmatex", + "pymdownx.magiclink", + "pymdownx.betterem", + "pymdownx.details", + "pymdownx.emoji", + "pymdownx.inlinehilite", + "pymdownx.superfences", + "pymdownx.tasklist", + "markdown.extensions.footnotes", + "markdown.extensions.attr_list", + "markdown.extensions.def_list", + "markdown.extensions.tables", + "markdown.extensions.admonition", + "nl2br", + "mdx_breakless_lists", +] + +EXTENSION_CONFIGS = { + "pymdownx.superfences": { + "custom_fences": [ + { + "name": "sample", + "class": "no-border", + "format": superfences.fence_code_format, + } + ] + }, +} + +ALLOWED_TAGS = bleach.sanitizer.ALLOWED_TAGS + [ + "img", + "center", + "iframe", + "div", + "span", + "table", + "tr", + "td", + "th", + "tr", + "pre", + "code", + "p", + "hr", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "thead", + "tbody", + "sup", + "dl", + "dt", + "dd", + "br", + "details", + "summary", +] + +ALLOWED_ATTRS = ["src", "width", "height", "href", "class", "open"] @registry.filter def markdown(value, lazy_load=False): - return _markdown(value, lazy_load) + extensions = EXTENSIONS + html = _markdown.markdown( + value, extensions=extensions, extension_configs=EXTENSION_CONFIGS + ) + + # Don't clean mathjax + hash_script_tag = {} + soup = BeautifulSoup(html, "html.parser") + for script_tag in soup.find_all("script"): + allow_math_types = ["math/tex", "math/tex; mode=display"] + if script_tag.attrs.get("type", False) in allow_math_types: + hash_script_tag[str(hash(str(script_tag)))] = str(script_tag) + + for hashed_tag in hash_script_tag: + tag = hash_script_tag[hashed_tag] + html = html.replace(tag, hashed_tag) + + html = bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS) + + for hashed_tag in hash_script_tag: + tag = hash_script_tag[hashed_tag] + html = html.replace(hashed_tag, tag) + + if not html: + html = escape(value) + if lazy_load or True: + soup = BeautifulSoup(html, features="html.parser") + for img in soup.findAll("img"): + if img.get("src"): + img["data-src"] = img["src"] + img["src"] = "" + for img in soup.findAll("iframe"): + if img.get("src"): + img["data-src"] = img["src"] + img["src"] = "" + html = str(soup) + return '
{bef} => {aft}
\n" - - return render( - request, - "test_formatter/edit_test_formatter.html", - { - "title": _("Test Formatter"), - "check": 0, - "files_list": bef_file, - "file_name": filename, - "res": response, - }, - ) - - def post(self, request, *args, **kwargs): - action = request.POST.get("action") - if action == "convert": - try: - file = TestFormatterModel.objects.last() - filestr = str(file.file) - filename = filestr.split("/")[-1] - filepath = filestr.split("/")[0] - bef_inp_format = request.POST["bef_inp_format"] - bef_out_format = request.POST["bef_out_format"] - aft_inp_format = request.POST["aft_inp_format"] - aft_out_format = request.POST["aft_out_format"] - aft_file_name = request.POST["file_name"] - except KeyError: - return HttpResponseBadRequest("No data.") - - if filename != aft_file_name: - source_path = os.path.join(settings.MEDIA_ROOT, filestr) - new_path = os.path.join( - settings.MEDIA_ROOT, "test_formatter/" + aft_file_name - ) - os.rename(source_path, new_path) - filename = aft_file_name - - preview_data = { - "bef_inp_format": bef_inp_format, - "bef_out_format": bef_out_format, - "aft_inp_format": aft_inp_format, - "aft_out_format": aft_out_format, - "file_name": filename, - "file_path": filepath, - "file_str": filepath + "/" + filename, - } - - converted_zip = tf_logic.convert(preview_data) - - global file_path - file_path = converted_zip["file_path"] - - zip_instance = TestFormatterModel() - zip_instance.file = file_path - zip_instance.save() - - preview = tf_logic.preview(preview_data) - response = HttpResponse() - - for i in range(len(preview["bef_preview"])): - bef = preview["bef_preview"][i]["value"] - aft = preview["aft_preview"][i]["value"] - response.write(f"{bef} => {aft}
") - - return response - - elif action == "download": - return HttpResponse(file_path) - - return HttpResponseBadRequest("Invalid action") - - -class DownloadTestFormatter(View): - def get(self, request): - file_path = request.GET.get("file_path") - file_name = file_path.split("/")[-1] - preview_file = tf_logic.preview_file(file_path) - - response = "" - for i in range(len(preview_file)): - response = response + (f"{preview_file[i]}
\n") - - files_list = [preview_file[0], preview_file[1]] - - return render( - request, - "test_formatter/download_test_formatter.html", - { - "title": _("Test Formatter"), - "response": response, - "files_list": files_list, - "file_path": os.path.join(settings.MEDIA_ROOT, file_path), - "file_path_getnames": file_path, - "file_name": file_name, - }, - ) - - def post(self, request): - file_path = request.POST.get("file_path") - - with open(file_path, "rb") as zip_file: - response = HttpResponse(zip_file.read(), content_type="application/zip") - response[ - "Content-Disposition" - ] = f"attachment; filename={os.path.basename(file_path)}" - return response diff --git a/judge/views/test_formatter/tf_logic.py b/judge/views/test_formatter/tf_logic.py deleted file mode 100644 index f981745..0000000 --- a/judge/views/test_formatter/tf_logic.py +++ /dev/null @@ -1,116 +0,0 @@ -import os -from judge.views.test_formatter import test_formatter as tf -from judge.views.test_formatter import tf_pattern as pattern - - -class TestSuite: - def __init__( - self, - file_id: str, - pattern_pair: pattern.PatternPair, - test_id_list: list, - extra_files: list, - ): - self.file_id = file_id - self.pattern_pair = pattern_pair - self.test_id_list = test_id_list - self.extra_files = extra_files - - @classmethod - def get_test_suite(cls, file_name: str, inp_format: str, out_format: str): - pattern_pair = pattern.PatternPair.from_string_pair(inp_format, out_format) - names = tf.get_names_in_archive(file_name) - test_id_list, extra_files = pattern_pair.matches( - names, returns="test_id_with_extra_files" - ) - return cls(file_name, pattern_pair, test_id_list, extra_files) - - def get_name_list(self, add_extra_info=False): - important_files = [] - - for index, t in enumerate(self.test_id_list): - inp_name = self.pattern_pair.x.get_name(t, index=index, use_index=True) - out_name = self.pattern_pair.y.get_name(t, index=index, use_index=True) - important_files.extend([inp_name, out_name]) - - result = [] - - for name in important_files: - if add_extra_info: - result.append({"value": name, "is_extra_file": False}) - else: - result.append(name) - - for name in self.extra_files: - if add_extra_info: - result.append({"value": name, "is_extra_file": True}) - else: - result.append(name) - - return result - - -def is_valid_file_type(file_name): - _, ext = os.path.splitext(file_name) - return ext in [".zip", ".ZIP"] - - -def preview(params): - bif = params["bef_inp_format"] - bof = params["bef_out_format"] - aif = params["aft_inp_format"] - aof = params["aft_out_format"] - file_str = params["file_str"] - - try: - test_suite = TestSuite.get_test_suite(file_str, bif, bof) - bef_preview = test_suite.get_name_list(add_extra_info=True) - try: - test_suite.pattern_pair = pattern.PatternPair.from_string_pair(aif, aof) - aft_preview = test_suite.get_name_list(add_extra_info=True) - return {"bef_preview": bef_preview, "aft_preview": aft_preview} - except: - return {"bef_preview": bef_preview, "aft_preview": []} - except: - test_suite = TestSuite.get_test_suite(file_id, "*", "*") - preview = test_suite.get_name_list(add_extra_info=True) - return {"bef_preview": preview, "aft_preview": []} - - -def convert(params): - bif = params["bef_inp_format"] - bof = params["bef_out_format"] - aif = params["aft_inp_format"] - aof = params["aft_out_format"] - file_str = params["file_str"] - file_name = params["file_name"] - file_path = params["file_path"] - - test_suite = TestSuite.get_test_suite(file_str, bif, bof) - bef_preview = test_suite.get_name_list() - test_suite.pattern_pair = pattern.PatternPair.from_string_pair(aif, aof) - aft_preview = test_suite.get_name_list() - - result = tf.get_renamed_archive( - file_str, file_name, file_path, bef_preview, aft_preview - ) - return result - - -def prefill(params): - file_str = params["file_str"] - file_name = params["file_name"] - - names = tf.get_names_in_archive(file_str) - pattern_pair = pattern.find_best_pattern_pair(names) - - return { - "file_name": file_name, - "inp_format": pattern_pair.x.to_string(), - "out_format": pattern_pair.y.to_string(), - } - - -def preview_file(file_str): - names = tf.get_names_in_archive(file_str) - return names diff --git a/judge/views/test_formatter/tf_pattern.py b/judge/views/test_formatter/tf_pattern.py deleted file mode 100644 index 071976d..0000000 --- a/judge/views/test_formatter/tf_pattern.py +++ /dev/null @@ -1,268 +0,0 @@ -import os -import random -from judge.views.test_formatter import tf_utils as utils - -SAMPLE_SIZE = 16 -NUMBERED_MM = ["0", "1", "00", "01", "000", "001", "0000", "0001"] -VALID_MM = ["*"] + NUMBERED_MM - -MSG_TOO_MANY_OCCURRENCES = ( - "400: Invalid pattern: Pattern cannot have more than one '{}'" -) -MSG_MM_NOT_FOUND = "400: Invalid pattern: Wildcard not found. Wildcard list: {}" - - -class Pattern: - def __init__(self, ll, mm, rr): - assert mm in VALID_MM, "Invalid wildcard" - self.ll = ll - self.mm = mm - self.rr = rr - - def __repr__(self): - return "Pattern('{}', '{}', '{}')".format(self.ll, self.mm, self.rr) - - def __eq__(self, other): - return self.__repr__() == other.__repr__() - - def __hash__(self): - return self.__repr__().__hash__() - - @classmethod - def from_string(cls, text): - for mm in ["*"] + sorted(NUMBERED_MM, key=len, reverse=True): - if mm in text: - if text.count(mm) > 1: - raise Exception(MSG_TOO_MANY_OCCURRENCES.format(mm)) - i = text.index(mm) - return cls(text[:i], mm, text[i + len(mm) :]) - raise Exception(MSG_MM_NOT_FOUND.format(",".join(VALID_MM))) - - def to_string(self): - return self.ll + self.mm + self.rr - - def is_valid_test_id(self, test_id): - if self.mm == "*": - return True - if self.mm in NUMBERED_MM: - return test_id.isdigit() and len(test_id) >= len(self.mm) - raise NotImplementedError - - def matched(self, name): - return ( - name.startswith(self.ll) - and name.endswith(self.rr) - and len(name) >= len(self.ll) + len(self.rr) - and self.is_valid_test_id(self.get_test_id(name)) - ) - - def get_test_id(self, name): - return name[len(self.ll) : len(name) - len(self.rr)] - - def get_test_id_from_index(self, index): - assert self.mm in NUMBERED_MM, "Wildcard is not a number" - return str(int(self.mm) + index).zfill(len(self.mm)) - - def get_name(self, test_id, index=None, use_index=False): - if use_index and self.mm in NUMBERED_MM: - return self.ll + self.get_test_id_from_index(index) + self.rr - return self.ll + test_id + self.rr - - def matches(self, names, returns): - if returns == "test_id": - result = [n for n in names] - result = [n for n in result if self.matched(n)] - result = [self.get_test_id(n) for n in result] - return result - else: - raise NotImplementedError - - -class PatternPair: - def __init__(self, x: Pattern, y: Pattern): - assert x.mm == y.mm, "Input wildcard and output wildcard must be equal" - self.x = x - self.y = y - - def __repr__(self): - return "PatternPair({}, {})".format(self.x, self.y) - - def __eq__(self, other): - return self.__repr__() == other.__repr__() - - def __hash__(self): - return self.__repr__().__hash__() - - @classmethod - def from_string_pair(cls, inp_format, out_format): - return cls(Pattern.from_string(inp_format), Pattern.from_string(out_format)) - - def matches(self, names, returns): - x_test_ids = self.x.matches(names, returns="test_id") - y_test_ids = self.y.matches(names, returns="test_id") - - test_ids = set(x_test_ids) & set(y_test_ids) - test_ids = list(sorted(test_ids, key=utils.natural_sorting_key)) - - if returns == "fast_count": - if self.x.mm == "*": - return len(test_ids) - elif self.x.mm in NUMBERED_MM: - count_valid = 0 - for t in test_ids: - if t == self.x.get_test_id_from_index(count_valid): - count_valid += 1 - - return count_valid - - extra_files = list(names) - valid_test_ids = [] - for t in test_ids: - if self.x.mm in NUMBERED_MM: - if t != self.x.get_test_id_from_index(len(valid_test_ids)): - continue - - inp_name = self.x.get_name(t) - out_name = self.y.get_name(t) - - if inp_name == out_name: - continue - if inp_name not in extra_files: - continue - if out_name not in extra_files: - continue - - valid_test_ids.append(t) - extra_files.remove(inp_name) - extra_files.remove(out_name) - - if returns == "count": - return len(valid_test_ids) - elif returns == "test_id": - return valid_test_ids - elif returns == "test_id_with_extra_files": - return valid_test_ids, extra_files - else: - raise NotImplementedError - - def score(self, names): - def ls(s): - return len(s) - s.count("0") - - def zs(s): - return -s.count("0") - - def vs(s): - return sum( - s.lower().count(c) * w - for c, w in [("a", -1), ("e", -1), ("i", +1), ("o", -1), ("u", -1)] - ) - - count_score = self.matches(names, returns="fast_count") - - len_score = ls(self.x.ll + self.x.rr + self.y.ll + self.y.rr) - zero_score = zs(self.x.ll + self.x.rr + self.y.ll + self.y.rr) - - assert self.x.mm in ["*"] + NUMBERED_MM - specific_score = 0 if self.x.mm == "*" else len(self.x.mm) - - vowel_score = vs(self.x.ll + self.x.rr) - vs(self.y.ll + self.y.rr) - - return count_score, specific_score, len_score, zero_score, vowel_score - - def is_string_safe(self): - try: - x = Pattern.from_string(self.x.to_string()) - y = Pattern.from_string(self.y.to_string()) - return self == PatternPair(x, y) - except: - return False - - -def maximal(a, key): - max_score = max(map(key, a)) - result = [x for x in a if key(x) == max_score] - if len(result) == 1: - return result[0] - else: - print(result) - raise Exception("More than one maximum values") - - -def get_all_star_pattern_pairs(names): - sample = random.sample(names, min(len(names), SAMPLE_SIZE)) - - star_pattern_pairs = [] - - all_prefixes = [n[:i] for n in sample for i in range(len(n) + 1)] - all_prefixes = list(sorted(set(all_prefixes))) - all_suffixes = [n[i:] for n in sample for i in range(len(n) + 1)] - all_suffixes = list(sorted(set(all_suffixes))) - - for prefix in all_prefixes: - matched_names = [n for n in names if n.startswith(prefix)] - if len(matched_names) == 2: - mn0, mn1 = matched_names - for i in range(len(prefix) + 1): - x = Pattern(prefix[:i], "*", mn0[len(prefix) :]) - y = Pattern(prefix[:i], "*", mn1[len(prefix) :]) - star_pattern_pairs.append(PatternPair(x, y)) - - for suffix in all_suffixes: - matched_names = [n for n in names if n.endswith(suffix)] - if len(matched_names) == 2: - mn0, mn1 = matched_names - for i in range(len(suffix) + 1): - x = Pattern(mn0[: len(mn0) - len(suffix)], "*", suffix[i:]) - y = Pattern(mn1[: len(mn1) - len(suffix)], "*", suffix[i:]) - star_pattern_pairs.append(PatternPair(x, y)) - - star_pattern_pairs = list(set(star_pattern_pairs)) - return star_pattern_pairs - - -def get_variant_pattern_pairs(pp): - return [ - PatternPair(Pattern(pp.x.ll, mm, pp.x.rr), Pattern(pp.y.ll, mm, pp.y.rr)) - for mm in VALID_MM - ] + [ - PatternPair(Pattern(pp.y.ll, mm, pp.y.rr), Pattern(pp.x.ll, mm, pp.x.rr)) - for mm in VALID_MM - ] - - -def find_best_pattern_pair(names): - star_pattern_pairs = get_all_star_pattern_pairs(names) - star_pattern_pairs = [ - pp for pp in star_pattern_pairs if pp.matches(names, returns="fast_count") >= 2 - ] - # for pp in star_pattern_pairs: - # print(pp, pp.is_string_safe(), pp.score(names)) - - if len(star_pattern_pairs) == 0: - return PatternPair(Pattern("", "*", ""), Pattern("", "*", "")) - best_star_pattern_pair = maximal(star_pattern_pairs, key=lambda pp: pp.score(names)) - - pattern_pairs = get_variant_pattern_pairs(best_star_pattern_pair) - # for pp in pattern_pairs: - # print(pp, pp.is_string_safe(), pp.score(names)) - pattern_pairs = [pp for pp in pattern_pairs if pp.is_string_safe()] - best_pattern_pair = maximal(pattern_pairs, key=lambda pp: pp.score(names)) - - return best_pattern_pair - - -def list_dir_recursively(folder): - old_cwd = os.getcwd() - os.chdir(folder) - result = [] - for root, _, filenames in os.walk("."): - for filename in filenames: - result.append(os.path.join(root, filename)) - os.chdir(old_cwd) - return result - - -def test_with_dir(folder): - names = list_dir_recursively(folder) - print(folder, find_best_pattern_pair(names)) diff --git a/judge/views/test_formatter/tf_utils.py b/judge/views/test_formatter/tf_utils.py deleted file mode 100644 index 919b069..0000000 --- a/judge/views/test_formatter/tf_utils.py +++ /dev/null @@ -1,15 +0,0 @@ -def get_char_kind(char): - return 1 if char.isdigit() else 2 if char.isalpha() else 3 - - -def natural_sorting_key(name): - result = [] - last_kind = -1 - for char in name: - curr_kind = get_char_kind(char) - if curr_kind != last_kind: - result.append("") - result[-1] += char - last_kind = curr_kind - - return [x.zfill(16) if x.isdigit() else x for x in result] diff --git a/judge/views/ticket.py b/judge/views/ticket.py index 60a151e..3d62235 100644 --- a/judge/views/ticket.py +++ b/judge/views/ticket.py @@ -35,7 +35,6 @@ from judge.utils.tickets import filter_visible_tickets, own_ticket_filter from judge.utils.views import SingleObjectFormView, TitleMixin, paginate_query_context from judge.views.problem import ProblemMixin from judge.widgets import HeavyPreviewPageDownWidget -from judge.models.notification import make_notification ticket_widget = ( forms.Textarea() @@ -50,10 +49,16 @@ ticket_widget = ( def add_ticket_notifications(users, author, link, ticket): html = f'{ticket.linked_item}' + users = set(users) if author in users: users.remove(author) - make_notification(users, "Ticket", html, author) + + for user in users: + notification = Notification( + owner=user, html_link=html, category="Ticket", author=author + ) + notification.save() class TicketForm(forms.Form): diff --git a/judge/views/user.py b/judge/views/user.py index 6feaffa..80f9add 100644 --- a/judge/views/user.py +++ b/judge/views/user.py @@ -35,42 +35,23 @@ from django.views.generic import DetailView, ListView, TemplateView from django.template.loader import render_to_string from reversion import revisions -from judge.forms import UserForm, ProfileForm, ProfileInfoForm -from judge.models import ( - Profile, - Rating, - Submission, - Friend, - ProfileInfo, - BlogPost, - Problem, - Contest, - Solution, -) +from judge.forms import UserForm, ProfileForm +from judge.models import Profile, Rating, Submission, Friend from judge.performance_points import get_pp_breakdown from judge.ratings import rating_class, rating_progress from judge.tasks import import_users from judge.utils.problems import contest_completed_ids, user_completed_ids from judge.utils.ranker import ranker from judge.utils.unicode import utf8text -from judge.utils.users import ( - get_rating_rank, - get_points_rank, - get_awards, - get_contest_ratings, -) from judge.utils.views import ( + DiggPaginatorMixin, QueryStringSortMixin, TitleMixin, generic_message, SingleObjectFormView, - DiggPaginatorMixin, ) -from judge.utils.infinite_paginator import InfinitePaginationMixin -from judge.views.problem import ProblemList from .contests import ContestRanking - __all__ = [ "UserPage", "UserAboutPage", @@ -119,12 +100,12 @@ class UserPage(TitleMixin, UserMixin, DetailView): def get_title(self): return ( _("My account") - if self.request.profile == self.object - else _("User %s") % self.object.username + if self.request.user == self.object.user + else _("User %s") % self.object.user.username ) def get_content_title(self): - username = self.object.username + username = self.object.user.username css_class = self.object.css_class return mark_safe(f'{username}') @@ -161,10 +142,22 @@ class UserPage(TitleMixin, UserMixin, DetailView): rating = self.object.ratings.order_by("-contest__end_time")[:1] context["rating"] = rating[0] if rating else None - context["points_rank"] = get_points_rank(self.object) + context["rank"] = ( + Profile.objects.filter( + is_unlisted=False, + performance_points__gt=self.object.performance_points, + ).count() + + 1 + ) if rating: - context["rating_rank"] = get_rating_rank(self.object) + context["rating_rank"] = ( + Profile.objects.filter( + is_unlisted=False, + rating__gt=self.object.rating, + ).count() + + 1 + ) context["rated_users"] = Profile.objects.filter( is_unlisted=False, rating__isnull=False ).count() @@ -192,9 +185,42 @@ EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) class UserAboutPage(UserPage): template_name = "user/user-about.html" + def get_awards(self, ratings): + result = {} + + sorted_ratings = sorted( + ratings, key=lambda x: (x.rank, -x.contest.end_time.timestamp()) + ) + + result["medals"] = [ + { + "label": rating.contest.name, + "ranking": rating.rank, + "link": reverse("contest_ranking", args=(rating.contest.key,)) + + "#!" + + self.object.username, + "date": date_format(rating.contest.end_time, _("M j, Y")), + } + for rating in sorted_ratings + if rating.rank <= 3 + ] + + num_awards = 0 + for i in result: + num_awards += len(result[i]) + + if num_awards == 0: + result = None + + return result + def get_context_data(self, **kwargs): context = super(UserAboutPage, self).get_context_data(**kwargs) - ratings = context["ratings"] = get_contest_ratings(self.object) + ratings = context["ratings"] = ( + self.object.ratings.order_by("-contest__end_time") + .select_related("contest") + .defer("contest__description") + ) context["rating_data"] = mark_safe( json.dumps( @@ -203,9 +229,7 @@ class UserAboutPage(UserPage): "label": rating.contest.name, "rating": rating.rating, "ranking": rating.rank, - "link": reverse("contest_ranking", args=(rating.contest.key,)) - + "#!" - + self.object.username, + "link": reverse("contest_ranking", args=(rating.contest.key,)), "timestamp": (rating.contest.end_time - EPOCH).total_seconds() * 1000, "date": date_format( @@ -220,7 +244,7 @@ class UserAboutPage(UserPage): ) ) - context["awards"] = get_awards(self.object) + context["awards"] = self.get_awards(ratings) if ratings: user_data = self.object.ratings.aggregate(Min("rating"), Max("rating")) @@ -264,6 +288,19 @@ class UserAboutPage(UserPage): return context + # follow/unfollow user + def post(self, request, user, *args, **kwargs): + try: + if not request.profile: + raise Exception("You have to login") + if request.profile.username == user: + raise Exception("Cannot make friend with yourself") + + following_profile = Profile.objects.get(user__username=user) + Friend.toggle_friend(request.profile, following_profile) + finally: + return HttpResponseRedirect(request.path_info) + class UserProblemsPage(UserPage): template_name = "user/user-problems.html" @@ -320,49 +357,17 @@ class UserProblemsPage(UserPage): return context -class UserBookMarkPage(DiggPaginatorMixin, ListView, UserPage): +class UserBookMarkPage(UserPage): template_name = "user/user-bookmarks.html" - context_object_name = "bookmarks" - paginate_by = 10 - - def get(self, request, *args, **kwargs): - self.current_tab = self.request.GET.get("tab", "problems") - self.user = self.object = self.get_object() - return super(UserBookMarkPage, self).get(request, *args, **kwargs) - - def get_queryset(self): - model = None - if self.current_tab == "posts": - model = BlogPost - elif self.current_tab == "contests": - model = Contest - elif self.current_tab == "editorials": - model = Solution - else: - model = Problem - - q = MakeBookMark.objects.filter(user=self.user).select_related("bookmark") - q = q.filter(bookmark__content_type=ContentType.objects.get_for_model(model)) - object_ids = q.values_list("bookmark__object_id", flat=True) - - res = model.objects.filter(id__in=object_ids) - if self.current_tab == "contests": - res = res.prefetch_related("organizations", "tags") - elif self.current_tab == "editorials": - res = res.select_related("problem") - - return res def get_context_data(self, **kwargs): context = super(UserBookMarkPage, self).get_context_data(**kwargs) - context["current_tab"] = self.current_tab - context["user"] = self.user - - context["page_prefix"] = ( - self.request.path + "?tab=" + self.current_tab + "&page=" - ) - context["first_page_href"] = self.request.path + bookmark_list = MakeBookMark.objects.filter(user=self.object) + context["blogs"] = bookmark_list.filter(bookmark__page__startswith="b") + context["problems"] = bookmark_list.filter(bookmark__page__startswith="p") + context["contests"] = bookmark_list.filter(bookmark__page__startswith="c") + context["solutions"] = bookmark_list.filter(bookmark__page__startswith="s") return context @@ -397,26 +402,22 @@ class UserPerformancePointsAjax(UserProblemsPage): @login_required def edit_profile(request): - profile = request.profile - profile_info, created = ProfileInfo.objects.get_or_create(profile=profile) + profile = Profile.objects.get(user=request.user) + if profile.mute: + raise Http404() if request.method == "POST": form_user = UserForm(request.POST, instance=request.user) - form = ProfileForm( - request.POST, request.FILES, instance=profile, user=request.user - ) - form_info = ProfileInfoForm(request.POST, instance=profile_info) + form = ProfileForm(request.POST, instance=profile, user=request.user) if form_user.is_valid() and form.is_valid(): - with revisions.create_revision(): + with transaction.atomic(), revisions.create_revision(): form_user.save() form.save() - form_info.save() revisions.set_user(request.user) revisions.set_comment(_("Updated on site")) return HttpResponseRedirect(request.path) else: form_user = UserForm(instance=request.user) form = ProfileForm(instance=profile, user=request.user) - form_info = ProfileInfoForm(instance=profile_info) tzmap = settings.TIMEZONE_MAP @@ -427,16 +428,16 @@ def edit_profile(request): "require_staff_2fa": settings.DMOJ_REQUIRE_STAFF_2FA, "form_user": form_user, "form": form, - "form_info": form_info, "title": _("Edit profile"), "profile": profile, + "has_math_config": bool(settings.MATHOID_URL), "TIMEZONE_MAP": tzmap or "http://momentjs.com/static/img/world.png", "TIMEZONE_BG": settings.TIMEZONE_BG if tzmap else "#4E7CAD", }, ) -class UserList(QueryStringSortMixin, InfinitePaginationMixin, TitleMixin, ListView): +class UserList(QueryStringSortMixin, DiggPaginatorMixin, TitleMixin, ListView): model = Profile title = gettext_lazy("Leaderboard") context_object_name = "users" @@ -448,21 +449,22 @@ class UserList(QueryStringSortMixin, InfinitePaginationMixin, TitleMixin, ListVi filter_friend = False def filter_friend_queryset(self, queryset): - friends = self.request.profile.get_friends() - ret = queryset.filter(id__in=friends) + friends = list(self.request.profile.get_friends()) + ret = queryset.filter(user__username__in=friends) return ret def get_queryset(self): queryset = ( Profile.objects.filter(is_unlisted=False) .order_by(self.order, "id") + .select_related("user") .only( "display_rank", + "user__username", "points", "rating", "performance_points", "problem_count", - "about", ) ) if self.request.organization: @@ -470,11 +472,11 @@ class UserList(QueryStringSortMixin, InfinitePaginationMixin, TitleMixin, ListVi if (self.request.GET.get("friend") == "true") and self.request.profile: queryset = self.filter_friend_queryset(queryset) self.filter_friend = True + return queryset def get_context_data(self, **kwargs): context = super(UserList, self).get_context_data(**kwargs) - Profile.prefetch_profile_cache([u.id for u in context["users"]]) context["users"] = ranker( context["users"], rank=self.paginate_by * (context["page_obj"].number - 1) ) @@ -590,17 +592,3 @@ def toggle_darkmode(request): return HttpResponseBadRequest() request.session["darkmode"] = not request.session.get("darkmode", False) return HttpResponseRedirect(path) - - -@login_required -def toggle_follow(request, user): - if request.method != "POST": - raise Http404() - - profile_to_follow = get_object_or_404(Profile, user__username=user) - - if request.profile.id == profile_to_follow.id: - raise Http404() - - Friend.toggle_friend(request.profile, profile_to_follow) - return HttpResponseRedirect(reverse("user_page", args=(user,))) diff --git a/judge/views/volunteer.py b/judge/views/volunteer.py index acd3469..64c58f9 100644 --- a/judge/views/volunteer.py +++ b/judge/views/volunteer.py @@ -19,14 +19,15 @@ def vote_problem(request): except Exception as e: return HttpResponseBadRequest() - vote, _ = VolunteerProblemVote.objects.get_or_create( - voter=request.profile, - problem=problem, - defaults={"knowledge_points": 0, "thinking_points": 0}, - ) - vote.knowledge_points = knowledge_points - vote.thinking_points = thinking_points - vote.feedback = feedback - vote.types.set(types) - vote.save() + with transaction.atomic(): + vote, _ = VolunteerProblemVote.objects.get_or_create( + voter=request.profile, + problem=problem, + defaults={"knowledge_points": 0, "thinking_points": 0}, + ) + vote.knowledge_points = knowledge_points + vote.thinking_points = thinking_points + vote.feedback = feedback + vote.types.set(types) + vote.save() return JsonResponse({}) diff --git a/judge/widgets/__init__.py b/judge/widgets/__init__.py index a9455a9..cc25941 100644 --- a/judge/widgets/__init__.py +++ b/judge/widgets/__init__.py @@ -3,4 +3,3 @@ from judge.widgets.mixins import CompressorWidgetMixin from judge.widgets.pagedown import * from judge.widgets.select2 import * from judge.widgets.datetime import * -from judge.widgets.image import * diff --git a/judge/widgets/datetime.py b/judge/widgets/datetime.py index d205e1c..6cd7fc5 100644 --- a/judge/widgets/datetime.py +++ b/judge/widgets/datetime.py @@ -1,49 +1,24 @@ from django import forms -from django.templatetags.static import static -from django.utils.html import format_html -from django.forms.utils import flatatt -from django.utils.dateparse import parse_datetime, parse_date class DateTimePickerWidget(forms.DateTimeInput): - input_type = "datetime-local" + template_name = "widgets/datetimepicker.html" - def render(self, name, value, attrs=None, renderer=None): - if value is None: - value = "" - elif isinstance(value, str): - # Attempt to parse the string back to datetime - parsed_date = parse_datetime(value) - if parsed_date is not None: - value = parsed_date.strftime("%Y-%m-%dT%H:%M") - else: - value = "" - else: - value = value.strftime("%Y-%m-%dT%H:%M") + def get_context(self, name, value, attrs): + datetimepicker_id = "datetimepicker_{name}".format(name=name) + if attrs is None: + attrs = dict() + attrs["data-target"] = "#{id}".format(id=datetimepicker_id) + attrs["class"] = "form-control datetimepicker-input" + context = super().get_context(name, value, attrs) + context["widget"]["datetimepicker_id"] = datetimepicker_id + return context - final_attrs = self.build_attrs( - attrs, {"type": self.input_type, "name": name, "value": value} + @property + def media(self): + css_url = "https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.min.css" + js_url = "https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.full.min.js" + return forms.Media( + js=[js_url], + css={"screen": [css_url]}, ) - return format_html("", flatatt(final_attrs)) - - -class DatePickerWidget(forms.DateInput): - input_type = "date" - - def render(self, name, value, attrs=None, renderer=None): - if value is None: - value = "" - elif isinstance(value, str): - # Attempt to parse the string back to date - parsed_date = parse_date(value) - if parsed_date is not None: - value = parsed_date.strftime("%Y-%m-%d") - else: - value = "" - else: - value = value.strftime("%Y-%m-%d") - - final_attrs = self.build_attrs( - attrs, {"type": self.input_type, "name": name, "value": value} - ) - return format_html("", flatatt(final_attrs)) diff --git a/judge/widgets/image.py b/judge/widgets/image.py deleted file mode 100644 index 89b4fb4..0000000 --- a/judge/widgets/image.py +++ /dev/null @@ -1,16 +0,0 @@ -from django import forms - - -class ImageWidget(forms.ClearableFileInput): - template_name = "widgets/image.html" - - def __init__(self, attrs=None, width=80, height=80): - self.width = width - self.height = height - super().__init__(attrs) - - def get_context(self, name, value, attrs=None): - context = super().get_context(name, value, attrs) - context["widget"]["height"] = self.height - context["widget"]["width"] = self.height - return context diff --git a/judge/widgets/pagedown.py b/judge/widgets/pagedown.py index b0403d5..c661ddb 100644 --- a/judge/widgets/pagedown.py +++ b/judge/widgets/pagedown.py @@ -10,8 +10,8 @@ from judge.widgets.mixins import CompressorWidgetMixin __all__ = [ "PagedownWidget", "AdminPagedownWidget", - "KatexPagedownWidget", - "KatexAdminPagedownWidget", + "MathJaxPagedownWidget", + "MathJaxAdminPagedownWidget", "HeavyPreviewPageDownWidget", "HeavyPreviewAdminPageDownWidget", ] @@ -21,8 +21,8 @@ try: except ImportError: PagedownWidget = None AdminPagedownWidget = None - KatexPagedownWidget = None - KatexAdminPagedownWidget = None + MathJaxPagedownWidget = None + MathJaxAdminPagedownWidget = None HeavyPreviewPageDownWidget = None HeavyPreviewAdminPageDownWidget = None else: @@ -47,6 +47,11 @@ else: "pagedown-extra/Markdown.Extra.js", "pagedown_init.js", ] + css = { + "all": [ + "markdown.css", + ] + } class AdminPagedownWidget(PagedownWidget, admin_widgets.AdminTextareaWidget): class Media: @@ -55,25 +60,21 @@ else: "pagedown_widget.css", "content-description.css", "admin/css/pagedown.css", + "markdown.css", "pagedown.css", - "https://fonts.googleapis.com/css2?family=Fira+Code&family=Noto+Sans&display=swap", ] } js = ["admin/js/pagedown.js"] - class KatexPagedownWidget(PagedownWidget): + class MathJaxPagedownWidget(PagedownWidget): class Media: - css = { - "all": ["https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"] - } js = [ - "katex_config.js", - "https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js", - "https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js", + "mathjax3_config.js", + "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js", "pagedown_math.js", ] - class KatexAdminPagedownWidget(AdminPagedownWidget, KatexPagedownWidget): + class MathJaxAdminPagedownWidget(AdminPagedownWidget, MathJaxPagedownWidget): pass class HeavyPreviewPageDownWidget(PagedownWidget): @@ -116,13 +117,15 @@ else: js = ["dmmd-preview.js"] class HeavyPreviewAdminPageDownWidget( - KatexPagedownWidget, AdminPagedownWidget, HeavyPreviewPageDownWidget + AdminPagedownWidget, HeavyPreviewPageDownWidget ): class Media: css = { "all": [ + "pygment-github.css", "table.css", "ranks.css", + "markdown.css", "dmmd-preview.css", ] } diff --git a/locale/vi/LC_MESSAGES/django.po b/locale/vi/LC_MESSAGES/django.po index 60a2668..42dcc74 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: 2024-10-03 02:46+0700\n" +"POT-Creation-Date: 2023-02-05 20:37+0700\n" "PO-Revision-Date: 2021-07-20 03:44\n" "Last-Translator: Icyene\n" "Language-Team: Vietnamese\n" @@ -18,362 +18,309 @@ msgstr "" "X-Crowdin-Project-ID: 466004\n" "X-Crowdin-File-ID: 5\n" -#: chat_box/models.py:22 chat_box/models.py:84 -msgid "last seen" -msgstr "xem lần cuối" - -#: chat_box/models.py:55 chat_box/models.py:80 chat_box/models.py:96 -#: judge/admin/interface.py:151 judge/models/contest.py:722 -#: judge/models/contest.py:928 judge/models/course.py:129 -#: judge/models/profile.py:465 judge/models/profile.py:539 +#: chat_box/models.py:31 chat_box/models.py:51 chat_box/models.py:65 +#: judge/admin/interface.py:150 judge/models/contest.py:633 +#: judge/models/contest.py:842 judge/models/course.py:115 +#: judge/models/profile.py:368 judge/models/profile.py:444 msgid "user" msgstr "người dùng" -#: chat_box/models.py:57 judge/models/comment.py:45 -#: judge/models/notification.py:31 +#: chat_box/models.py:32 judge/models/comment.py:46 judge/models/comment.py:245 msgid "posted time" msgstr "thời gian đăng" -#: chat_box/models.py:59 judge/models/comment.py:50 +#: chat_box/models.py:33 judge/models/comment.py:54 msgid "body of comment" msgstr "nội dung bình luận" -#: chat_box/views.py:46 +#: chat_box/models.py:55 +msgid "last seen" +msgstr "xem lần cuối" + +#: chat_box/views.py:45 msgid "LQDOJ Chat" msgstr "" -#: chat_box/views.py:173 -msgid "Mute chat" -msgstr "" - -#: chat_box/views.py:450 -msgid "Recent" -msgstr "Gần đây" - -#: chat_box/views.py:454 templates/base.html:198 -#: templates/comments/content-list.html:72 -#: templates/contest/contest-list-tabs.html:6 -#: templates/contest/ranking-table.html:52 templates/course/left_sidebar.html:9 -#: templates/internal/problem/problem.html:63 -#: templates/organization/org-left-sidebar.html:12 -#: templates/organization/users-table.html:19 -#: templates/problem/left-sidebar.html:13 -#: templates/problem/problem-list-tabs.html:6 -#: templates/submission/info-base.html:12 templates/submission/list.html:394 -#: templates/submission/submission-list-tabs.html:15 -msgid "Admin" -msgstr "Admin" - -#: dmoj/settings.py:366 +#: dmoj/settings.py:361 msgid "Vietnamese" msgstr "Tiếng Việt" -#: dmoj/settings.py:367 +#: dmoj/settings.py:362 msgid "English" msgstr "" -#: dmoj/urls.py:107 -msgid "Activation key invalid" -msgstr "Mã kích hoạt không hợp lệ" - -#: dmoj/urls.py:112 -msgid "Register" -msgstr "Đăng ký" - -#: dmoj/urls.py:119 -msgid "Registration Completed" -msgstr "Đăng ký hoàn thành" - -#: dmoj/urls.py:127 -msgid "Registration not allowed" -msgstr "Đăng ký không thành công" - -#: dmoj/urls.py:135 +#: dmoj/urls.py:134 msgid "Login" msgstr "Đăng nhập" -#: dmoj/urls.py:221 templates/base.html:120 -#: templates/course/left_sidebar.html:2 +#: dmoj/urls.py:211 templates/base.html:209 #: templates/organization/org-left-sidebar.html:2 msgid "Home" msgstr "Trang chủ" -#: judge/admin/comments.py:57 +#: judge/admin/comments.py:46 #, python-format msgid "%d comment successfully hidden." msgid_plural "%d comments successfully hidden." msgstr[0] "Đã ẩn %d bình luận." -#: judge/admin/comments.py:64 +#: judge/admin/comments.py:53 msgid "Hide comments" msgstr "Ẩn bình luận" -#: judge/admin/comments.py:71 +#: judge/admin/comments.py:60 #, python-format msgid "%d comment successfully unhidden." msgid_plural "%d comments successfully unhidden." msgstr[0] "Không ẩn được %d bình luận." -#: judge/admin/comments.py:78 +#: judge/admin/comments.py:67 msgid "Unhide comments" msgstr "Hiện bình luận" -#: judge/admin/contest.py:45 +#: judge/admin/comments.py:76 +msgid "Associated page" +msgstr "Trang liên kết" + +#: judge/admin/contest.py:37 msgid "Included contests" msgstr "" -#: judge/admin/contest.py:87 judge/admin/volunteer.py:54 -#: templates/contest/clarification.html:42 templates/contest/contest.html:121 -#: templates/contest/moss.html:41 templates/internal/left-sidebar.html:2 -#: templates/internal/problem/problem.html:41 templates/problem/list.html:17 +#: judge/admin/contest.py:80 judge/admin/volunteer.py:54 +#: templates/contest/clarification.html:42 templates/contest/contest.html:101 +#: templates/contest/moss.html:41 templates/internal/base.html:29 +#: templates/internal/base.html:37 templates/problem/list.html:17 #: templates/problem/list.html:34 templates/problem/list.html:153 #: templates/user/user-problems.html:56 templates/user/user-problems.html:98 msgid "Problem" msgstr "Bài tập" -#: judge/admin/contest.py:183 templates/base.html:213 -#: templates/user/user-tabs.html:12 +#: judge/admin/contest.py:156 msgid "Settings" msgstr "Cài đặt" -#: judge/admin/contest.py:198 +#: judge/admin/contest.py:169 msgid "Scheduling" msgstr "" -#: judge/admin/contest.py:202 +#: judge/admin/contest.py:173 msgid "Details" msgstr "Chi tiết" -#: judge/admin/contest.py:214 templates/contest/macros.html:83 -#: templates/contest/macros.html:93 +#: judge/admin/contest.py:185 msgid "Format" msgstr "Thể thức" -#: judge/admin/contest.py:218 templates/contest/ranking-table.html:5 -#: templates/profile-table.html:14 templates/profile-table.html:37 -#: templates/user/user-about.html:15 templates/user/user-about.html:44 +#: judge/admin/contest.py:189 templates/contest/ranking-table.html:7 +#: templates/user/user-about.html:15 templates/user/user-about.html:45 msgid "Rating" msgstr "" -#: judge/admin/contest.py:230 +#: judge/admin/contest.py:201 msgid "Access" msgstr "Truy cập" -#: judge/admin/contest.py:240 judge/admin/problem.py:233 +#: judge/admin/contest.py:211 judge/admin/problem.py:218 msgid "Justice" msgstr "Xử phạt" -#: judge/admin/contest.py:368 +#: judge/admin/contest.py:331 #, python-format msgid "%d contest successfully marked as visible." msgid_plural "%d contests successfully marked as visible." msgstr[0] "%d kỳ thi đã được đánh dấu hiển thị." -#: judge/admin/contest.py:375 +#: judge/admin/contest.py:338 msgid "Mark contests as visible" msgstr "Đánh dấu hiển thị các kỳ thi" -#: judge/admin/contest.py:386 +#: judge/admin/contest.py:349 #, python-format msgid "%d contest successfully marked as hidden." msgid_plural "%d contests successfully marked as hidden." msgstr[0] "%d kỳ thi đã được đánh dấu ẩn." -#: judge/admin/contest.py:393 +#: judge/admin/contest.py:356 msgid "Mark contests as hidden" msgstr "Ẩn các kỳ thi" -#: judge/admin/contest.py:414 judge/admin/submission.py:241 +#: judge/admin/contest.py:377 judge/admin/submission.py:243 #, python-format msgid "%d submission was successfully scheduled for rejudging." msgid_plural "%d submissions were successfully scheduled for rejudging." msgstr[0] "%d bài nộp đã được lên lịch thành công để chấm lại." -#: judge/admin/contest.py:522 +#: judge/admin/contest.py:485 #, python-format msgid "%d participation recalculated." msgid_plural "%d participations recalculated." msgstr[0] "%d thí sinh đã được tính điểm lại." -#: judge/admin/contest.py:529 +#: judge/admin/contest.py:492 msgid "Recalculate results" msgstr "Tính toán lại kết quả" -#: judge/admin/contest.py:534 judge/admin/organization.py:98 +#: judge/admin/contest.py:497 judge/admin/organization.py:99 msgid "username" msgstr "tên đăng nhập" -#: judge/admin/contest.py:540 templates/base.html:253 +#: judge/admin/contest.py:503 templates/base.html:317 msgid "virtual" msgstr "ảo" -#: judge/admin/interface.py:35 judge/models/interface.py:51 +#: judge/admin/interface.py:35 judge/models/interface.py:46 msgid "link path" msgstr "đường dẫn" -#: judge/admin/interface.py:95 templates/course/lesson.html:10 +#: judge/admin/interface.py:94 msgid "Content" msgstr "Nội dung" -#: judge/admin/interface.py:96 +#: judge/admin/interface.py:95 msgid "Summary" msgstr "Tổng kết" -#: judge/admin/interface.py:218 +#: judge/admin/interface.py:217 msgid "object" msgstr "" -#: judge/admin/interface.py:228 +#: judge/admin/interface.py:227 msgid "Diff" msgstr "" -#: judge/admin/interface.py:233 +#: judge/admin/interface.py:232 msgid "diff" msgstr "" -#: judge/admin/organization.py:60 judge/admin/problem.py:290 -#: judge/admin/profile.py:128 +#: judge/admin/organization.py:61 judge/admin/problem.py:275 +#: judge/admin/profile.py:116 msgid "View on site" msgstr "Xem trên trang" -#: judge/admin/problem.py:56 +#: judge/admin/problem.py:53 msgid "Describe the changes you made (optional)" msgstr "Mô tả các thay đổi (tùy chọn)" -#: judge/admin/problem.py:66 -#, fuzzy -#| msgid "Problem with code already exists." -msgid "A problem with this code already exists." -msgstr "Mã bài đã tồn tại." - -#: judge/admin/problem.py:122 +#: judge/admin/problem.py:109 msgid "Memory unit" msgstr "Đơn vị bộ nhớ" -#: judge/admin/problem.py:226 +#: judge/admin/problem.py:211 msgid "Social Media" msgstr "Mạng Xã Hội" -#: judge/admin/problem.py:229 +#: judge/admin/problem.py:214 msgid "Taxonomy" msgstr "" -#: judge/admin/problem.py:230 judge/admin/problem.py:463 -#: judge/views/course.py:494 judge/views/course.py:579 -#: templates/contest/contest.html:122 -#: templates/contest/contests_summary.html:41 -#: templates/course/contest_list.html:21 templates/problem/data.html:535 +#: judge/admin/problem.py:215 judge/admin/problem.py:438 +#: templates/contest/contest.html:102 templates/problem/data.html:517 #: templates/problem/list.html:22 templates/problem/list.html:48 -#: templates/profile-table.html:31 templates/profile-table.html:41 #: templates/user/base-users-table.html:10 templates/user/user-about.html:36 -#: templates/user/user-about.html:50 templates/user/user-problems.html:58 +#: templates/user/user-about.html:52 templates/user/user-problems.html:58 msgid "Points" msgstr "Điểm" -#: judge/admin/problem.py:231 +#: judge/admin/problem.py:216 msgid "Limits" msgstr "Giới hạn" -#: judge/admin/problem.py:232 judge/admin/submission.py:351 -#: templates/base.html:164 templates/stats/tab.html:4 -#: templates/submission/list.html:345 +#: judge/admin/problem.py:217 judge/admin/submission.py:353 +#: templates/base.html:245 templates/stats/tab.html:4 +#: templates/submission/list.html:347 msgid "Language" msgstr "Ngôn ngữ" -#: judge/admin/problem.py:234 +#: judge/admin/problem.py:219 msgid "History" msgstr "Lịch sử" -#: judge/admin/problem.py:286 templates/problem/list-base.html:93 +#: judge/admin/problem.py:271 templates/problem/list-base.html:100 msgid "Authors" msgstr "Các tác giả" -#: judge/admin/problem.py:307 +#: judge/admin/problem.py:292 #, 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:314 +#: judge/admin/problem.py:299 msgid "Mark problems as public" msgstr "Công khai bài tập" -#: judge/admin/problem.py:323 +#: judge/admin/problem.py:308 #, 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:330 +#: judge/admin/problem.py:315 msgid "Mark problems as private" msgstr "Đánh dấu các bài tập là riêng tư" -#: judge/admin/problem.py:457 judge/admin/submission.py:314 +#: judge/admin/problem.py:432 judge/admin/submission.py:316 #: templates/problem/list.html:18 templates/problem/list.html:37 msgid "Problem code" msgstr "Mã bài" -#: judge/admin/problem.py:469 judge/admin/submission.py:320 +#: judge/admin/problem.py:444 judge/admin/submission.py:322 msgid "Problem name" msgstr "Tên bài" -#: judge/admin/problem.py:475 +#: judge/admin/problem.py:450 #, fuzzy #| msgid "contest rating" msgid "Voter rating" msgstr "rating kỳ thi" -#: judge/admin/problem.py:481 +#: judge/admin/problem.py:456 #, fuzzy #| msgid "Total points" msgid "Voter point" msgstr "Tổng điểm" -#: judge/admin/problem.py:487 +#: judge/admin/problem.py:462 msgid "Vote" msgstr "" -#: judge/admin/profile.py:47 +#: judge/admin/profile.py:40 msgid "timezone" msgstr "múi giờ" -#: judge/admin/profile.py:137 judge/admin/submission.py:327 -#: templates/notification/list.html:9 +#: judge/admin/profile.py:125 judge/admin/submission.py:329 +#: templates/notification/list.html:12 #: templates/organization/requests/log.html:9 #: templates/organization/requests/pending.html:19 -#: templates/ticket/list.html:265 +#: templates/ticket/list.html:263 msgid "User" msgstr "Thành viên" -#: judge/admin/profile.py:143 templates/registration/registration_form.html:40 -#: templates/user/edit-profile.html:123 templates/user/import/table_csv.html:8 +#: judge/admin/profile.py:131 templates/registration/registration_form.html:40 +#: templates/user/import/table_csv.html:8 msgid "Email" msgstr "Email" -#: judge/admin/profile.py:149 judge/views/register.py:36 +#: judge/admin/profile.py:137 judge/views/register.py:36 #: templates/registration/registration_form.html:68 -#: templates/user/edit-profile.html:147 +#: templates/user/edit-profile.html:113 msgid "Timezone" msgstr "Múi giờ" -#: judge/admin/profile.py:155 +#: judge/admin/profile.py:143 msgid "date joined" msgstr "ngày tham gia" -#: judge/admin/profile.py:165 +#: judge/admin/profile.py:153 #, python-format msgid "%d user have scores recalculated." msgid_plural "%d users have scores recalculated." msgstr[0] "%d người dùng đã được tính điểm lại." -#: judge/admin/profile.py:172 +#: judge/admin/profile.py:160 msgid "Recalculate scores" msgstr "Tính điểm lại" -#: judge/admin/profile.py:179 judge/admin/profile.py:186 -msgid "Username can only contain letters, digits, and underscores." -msgstr "Tên đăng nhập phải chứa ký tự, chữ số, hoặc dấu gạch dưới" - #: judge/admin/runtime.py:19 msgid "Disallowed problems" msgstr "Các bài tập không được cho phép" @@ -395,7 +342,7 @@ msgid "Capabilities" msgstr "Khả năng" #: judge/admin/submission.py:31 judge/admin/submission.py:53 -#: judge/admin/submission.py:338 +#: judge/admin/submission.py:340 msgid "None" msgstr "None" @@ -417,51 +364,48 @@ msgctxt "contest problem" msgid "%(problem)s in %(contest)s" msgstr "%(problem)s trong %(contest)s" -#: judge/admin/submission.py:213 judge/admin/submission.py:254 +#: judge/admin/submission.py:215 judge/admin/submission.py:256 msgid "You do not have the permission to rejudge submissions." msgstr "Bạn không có quyền chấm lại bài." -#: judge/admin/submission.py:225 +#: judge/admin/submission.py:227 msgid "You do not have the permission to rejudge THAT many submissions." msgstr "Bạn không có quyền chấm lại nhiều bài nộp như vậy." -#: judge/admin/submission.py:248 +#: judge/admin/submission.py:250 msgid "Rejudge the selected submissions" msgstr "Chấm lại các bài nộp đã chọn" -#: judge/admin/submission.py:302 judge/views/problem_manage.py:218 +#: judge/admin/submission.py:304 judge/views/problem_manage.py:212 #, python-format msgid "%d submission were successfully rescored." msgid_plural "%d submissions were successfully rescored." msgstr[0] "%d bài nộp đã được tính điểm lại." -#: judge/admin/submission.py:309 +#: judge/admin/submission.py:311 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:175 -#: templates/contest/list.html:217 templates/contest/list.html:254 -#: templates/contest/list.html:288 templates/notification/list.html:12 +#: judge/admin/submission.py:334 templates/notification/list.html:15 #: templates/organization/requests/log.html:10 #: templates/organization/requests/pending.html:20 #: templates/problem/list.html:154 -#: templates/submission/status-testcases.html:139 -#: templates/submission/status-testcases.html:141 -#: templates/user/user-bookmarks.html:84 +#: templates/submission/status-testcases.html:144 +#: templates/submission/status-testcases.html:146 msgid "Time" msgstr "Thời gian" -#: judge/admin/submission.py:340 +#: judge/admin/submission.py:342 #, python-format msgid "%d KB" msgstr "%d KB" -#: judge/admin/submission.py:342 +#: judge/admin/submission.py:344 #, python-format msgid "%.2f MB" msgstr "" -#: judge/admin/submission.py:345 templates/submission/status-testcases.html:146 +#: judge/admin/submission.py:347 templates/submission/status-testcases.html:151 msgid "Memory" msgstr "Bộ nhớ" @@ -477,7 +421,7 @@ msgstr "Các bài tập trong nhóm này" msgid "These problems are included in this type of problems" msgstr "Các bài tập dạng này" -#: judge/admin/volunteer.py:60 templates/internal/problem/votes.html:17 +#: judge/admin/volunteer.py:60 templates/internal/base.html:78 #: templates/problem/list.html:20 templates/problem/list.html:44 msgid "Types" msgstr "Dạng" @@ -486,6 +430,23 @@ msgstr "Dạng" msgid "Online Judge" msgstr "" +#: judge/comments.py:60 +msgid "Comment body" +msgstr "Nội dung bình luận" + +#: judge/comments.py:66 judge/views/ticket.py:78 +msgid "Your part is silent, little toad." +msgstr "Bạn không được phép bình luận." + +#: judge/comments.py:75 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:128 +msgid "Posted comment" +msgstr "Bình luận đã đăng" + #: judge/contest_format/atcoder.py:19 msgid "AtCoder" msgstr "" @@ -510,311 +471,306 @@ msgstr "" msgid "New IOI" msgstr "IOI mới" -#: judge/contest_format/ultimate.py:12 -msgid "Ultimate" -msgstr "" +#: judge/forms.py:102 judge/views/organization.py:550 +#: judge/views/register.py:62 +#, python-brace-format +msgid "You may not be part of more than {count} public groups." +msgstr "Bạn không thể tham gia nhiều hơn {count} nhóm công khai." -#: judge/custom_translations.py:8 -#, python-format -msgid "" -"This password is too short. It must contain at least %(min_length)d " -"character." -msgid_plural "" -"This password is too short. It must contain at least %(min_length)d " -"characters." -msgstr[0] "Mật khẩu phải chứa ít nhất %(min_length)d ký tự." - -#: judge/custom_translations.py:13 -#, python-format -msgid "Your password must contain at least %(min_length)d character." -msgid_plural "Your password must contain at least %(min_length)d characters." -msgstr[0] "Mật khẩu phải chứa ít nhất %(min_length)d ký tự." - -#: judge/custom_translations.py:17 -msgid "The two password fields didn’t match." -msgstr "Mật khẩu xác nhận không khớp." - -#: judge/custom_translations.py:18 -msgid "Your password can’t be entirely numeric." -msgstr "Mật khẩu không được toàn chữ số." - -#: judge/custom_translations.py:20 -msgid "Bug Report" -msgstr "Báo cáo lỗi" - -#: judge/custom_translations.py:21 judge/views/course.py:164 -#: templates/course/list.html:8 -msgid "Courses" -msgstr "Khóa học" - -#: judge/forms.py:120 judge/forms.py:220 -msgid "File size exceeds the maximum allowed limit of 5MB." -msgstr "File tải lên không được quá 5MB." - -#: judge/forms.py:151 +#: judge/forms.py:134 msgid "Any judge" msgstr "" -#: judge/forms.py:368 +#: judge/forms.py:309 msgid "Enter usernames separating by space" msgstr "Nhập các tên đăng nhập, cách nhau bởi dấu cách" -#: judge/forms.py:369 judge/views/stats.py:166 templates/stats/site.html:27 +#: judge/forms.py:310 judge/views/stats.py:166 templates/stats/site.html:27 msgid "New users" msgstr "Thành viên mới" -#: judge/forms.py:386 +#: judge/forms.py:327 #, python-brace-format msgid "These usernames don't exist: {usernames}" msgstr "Các tên đăng nhập này không tồn tại: {usernames}" -#: judge/forms.py:445 -msgid "Username/Email" -msgstr "Tên đăng nhập / Email" +#: judge/forms.py:386 judge/views/register.py:30 +#: templates/registration/registration_form.html:34 +#: templates/user/base-users-table.html:5 +#: templates/user/import/table_csv.html:4 +msgid "Username" +msgstr "Tên đăng nhập" -#: judge/forms.py:447 judge/views/email.py:22 -#: templates/registration/registration_form.html:46 +#: judge/forms.py:387 templates/registration/registration_form.html:46 #: templates/registration/registration_form.html:60 -#: templates/user/edit-profile.html:115 templates/user/import/table_csv.html:5 +#: templates/user/import/table_csv.html:5 msgid "Password" msgstr "Mật khẩu" -#: judge/forms.py:473 +#: judge/forms.py:413 msgid "Two Factor Authentication tokens must be 6 decimal digits." msgstr "Two Factor Authentication phải chứa 6 chữ số." -#: judge/forms.py:486 templates/registration/totp_auth.html:32 +#: judge/forms.py:426 templates/registration/totp_auth.html:32 msgid "Invalid Two Factor Authentication token." msgstr "Token Two Factor Authentication không hợp lệ." -#: judge/forms.py:493 judge/models/problem.py:133 +#: judge/forms.py:433 judge/models/problem.py:130 msgid "Problem code must be ^[a-z0-9]+$" msgstr "Mã bài phải có dạng ^[a-z0-9]+$" -#: judge/forms.py:500 +#: judge/forms.py:440 msgid "Problem with code already exists." msgstr "Mã bài đã tồn tại." -#: judge/forms.py:507 judge/models/contest.py:103 +#: judge/forms.py:447 judge/models/contest.py:90 msgid "Contest id must be ^[a-z0-9]+$" msgstr "Mã kỳ thi phải có dạng ^[a-z0-9]+$" -#: judge/forms.py:514 templates/contest/clone.html:47 -#: templates/contest/search-form.html:12 templates/problem/search-form.html:39 -msgid "Group" -msgstr "Nhóm" - -#: judge/forms.py:522 +#: judge/forms.py:453 msgid "Contest with key already exists." msgstr "Mã kỳ thi đã tồn tại." -#: judge/forms.py:530 -msgid "Group doesn't exist." -msgstr "Nhóm không tồn tại." - -#: judge/forms.py:532 -msgid "You don't have permission in this group." -msgstr "Bạn không có quyền trong nhóm này." - -#: judge/forms.py:582 -msgid "This problem is duplicated." -msgstr "Bài này bị lặp" - -#: judge/jinja2/datetime.py:26 templates/blog/blog.html:28 +#: judge/jinja2/datetime.py:26 templates/blog/blog.html:29 #: templates/blog/dashboard.html:21 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:28 #, python-brace-format msgid "{time}" msgstr "{time}" -#: judge/middleware.py:135 +#: judge/jinja2/datetime.py:26 templates/blog/content.html:11 +#, python-brace-format +msgid "on {time}" +msgstr "vào {time}" + +#: judge/middleware.py:125 msgid "No permission" msgstr "Không có quyền truy cập" -#: judge/middleware.py:136 +#: judge/middleware.py:126 msgid "You need to join this group first" msgstr "Bạn phải là thành viên của nhóm." -#: judge/middleware.py:146 judge/middleware.py:147 +#: judge/middleware.py:136 judge/middleware.py:137 msgid "No such group" msgstr "Nhóm không tồn tại" -#: judge/models/bookmark.py:17 judge/models/comment.py:164 -#: judge/models/pagevote.py:16 +#: judge/models/bookmark.py:17 judge/models/comment.py:49 +#: judge/models/comment.py:226 judge/models/pagevote.py:13 msgid "associated page" msgstr "trang tương ứng" -#: judge/models/bookmark.py:20 judge/models/comment.py:49 -#: judge/models/pagevote.py:19 judge/models/problem.py:744 -msgid "votes" -msgstr "bình chọn" - -#: judge/models/bookmark.py:30 +#: judge/models/bookmark.py:44 +#, fuzzy +#| msgid "Bookmark" msgid "bookmark" msgstr "Lưu" -#: judge/models/bookmark.py:31 +#: judge/models/bookmark.py:45 +#, fuzzy +#| msgid "Bookmark" msgid "bookmarks" msgstr "Lưu" -#: judge/models/bookmark.py:52 +#: judge/models/bookmark.py:59 +#, fuzzy +#| msgid "Bookmark" msgid "make bookmark" msgstr "Lưu" -#: judge/models/bookmark.py:53 +#: judge/models/bookmark.py:60 +#, fuzzy +#| msgid "Bookmark" msgid "make bookmarks" msgstr "Lưu" -#: judge/models/comment.py:44 +#: judge/models/choices.py:59 +msgid "Leave as LaTeX" +msgstr "Để định dạng LaTeX" + +#: judge/models/choices.py:60 +msgid "SVG with PNG fallback" +msgstr "" + +#: judge/models/choices.py:61 +msgid "MathML only" +msgstr "chỉ dùng MathML" + +#: judge/models/choices.py:62 +msgid "MathJax with SVG/PNG fallback" +msgstr "" + +#: judge/models/choices.py:63 +msgid "Detect best quality" +msgstr "" + +#: judge/models/comment.py:26 +msgid "Page code must be ^[pcs]:[a-z0-9]+$|^b:\\d+$" +msgstr "Mã trang phải có dạng ^[pcs]:[a-z0-9]+$|^b:\\d+$" + +#: judge/models/comment.py:45 msgid "commenter" msgstr "người bình luận" -#: judge/models/comment.py:51 +#: judge/models/comment.py:53 judge/models/pagevote.py:16 +#: judge/models/problem.py:686 +msgid "votes" +msgstr "bình chọn" + +#: judge/models/comment.py:55 msgid "hide the comment" msgstr "ẩn bình luận" -#: judge/models/comment.py:54 +#: judge/models/comment.py:58 msgid "parent" msgstr "" -#: judge/models/comment.py:65 +#: judge/models/comment.py:67 judge/models/comment.py:247 msgid "comment" msgstr "bình luận" -#: judge/models/comment.py:66 +#: judge/models/comment.py:68 msgid "comments" msgstr "" -#: judge/models/comment.py:125 -msgid "Editorial for " -msgstr "Hướng dẫn cho {0}" +#: judge/models/comment.py:163 judge/models/problem.py:654 +#, python-format +msgid "Editorial for %s" +msgstr "" -#: judge/models/comment.py:157 +#: judge/models/comment.py:219 msgid "comment vote" msgstr "" -#: judge/models/comment.py:158 +#: judge/models/comment.py:220 msgid "comment votes" msgstr "" -#: judge/models/comment.py:169 +#: judge/models/comment.py:232 msgid "Override comment lock" msgstr "" -#: judge/models/contest.py:50 +#: judge/models/comment.py:241 +msgid "owner" +msgstr "" + +#: judge/models/comment.py:249 judge/models/message.py:28 +msgid "read" +msgstr "" + +#: judge/models/comment.py:250 +msgid "category" +msgstr "" + +#: judge/models/comment.py:253 +msgid "html link to comments, used for non-comments" +msgstr "" + +#: judge/models/comment.py:259 +msgid "who trigger, used for non-comment" +msgstr "" + +#: judge/models/contest.py:37 msgid "Invalid colour." msgstr "" -#: judge/models/contest.py:54 +#: judge/models/contest.py:41 msgid "tag name" msgstr "" -#: judge/models/contest.py:58 +#: judge/models/contest.py:45 msgid "Lowercase letters and hyphens only." msgstr "" -#: judge/models/contest.py:63 +#: judge/models/contest.py:50 msgid "tag colour" msgstr "" -#: judge/models/contest.py:65 +#: judge/models/contest.py:52 msgid "tag description" msgstr "" -#: judge/models/contest.py:86 +#: judge/models/contest.py:73 msgid "contest tag" msgstr "" -#: judge/models/contest.py:87 judge/models/contest.py:267 +#: judge/models/contest.py:74 judge/models/contest.py:242 msgid "contest tags" msgstr "nhãn kỳ thi" -#: judge/models/contest.py:95 +#: judge/models/contest.py:82 msgid "Visible" msgstr "Hiển thị" -#: judge/models/contest.py:96 +#: judge/models/contest.py:83 msgid "Hidden for duration of contest" msgstr "Ẩn trong thời gian kỳ thi" -#: judge/models/contest.py:97 +#: judge/models/contest.py:84 msgid "Hidden for duration of participation" msgstr "Ẩn trong thời gian tham gia" -#: judge/models/contest.py:101 +#: judge/models/contest.py:88 msgid "contest id" msgstr "ID kỳ thi" -#: judge/models/contest.py:106 +#: judge/models/contest.py:93 msgid "contest name" msgstr "tên kỳ thi" -#: judge/models/contest.py:110 judge/models/interface.py:80 -#: judge/models/problem.py:694 -msgid "authors" -msgstr "tác giả" - -#: judge/models/contest.py:111 +#: judge/models/contest.py:97 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:116 judge/models/problem.py:157 -msgid "curators" -msgstr "quản lý" - -#: judge/models/contest.py:118 +#: judge/models/contest.py:103 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:126 judge/models/problem.py:167 -msgid "testers" -msgstr "" - -#: judge/models/contest.py:128 +#: judge/models/contest.py:112 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:133 judge/models/runtime.py:217 +#: judge/models/contest.py:117 judge/models/course.py:158 +#: judge/models/runtime.py:211 msgid "description" msgstr "mô tả" -#: judge/models/contest.py:135 judge/models/problem.py:610 -#: judge/models/runtime.py:222 +#: judge/models/contest.py:119 judge/models/problem.py:559 +#: judge/models/runtime.py:216 msgid "problems" msgstr "bài tập" -#: judge/models/contest.py:137 judge/models/contest.py:727 +#: judge/models/contest.py:121 judge/models/contest.py:638 msgid "start time" msgstr "thời gian bắt đầu" -#: judge/models/contest.py:138 +#: judge/models/contest.py:122 msgid "end time" msgstr "thời gian kết thúc" -#: judge/models/contest.py:140 judge/models/problem.py:186 -#: judge/models/problem.py:645 +#: judge/models/contest.py:124 judge/models/problem.py:183 +#: judge/models/problem.py:594 msgid "time limit" msgstr "giới hạn thời gian" -#: judge/models/contest.py:144 +#: judge/models/contest.py:128 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:148 +#: judge/models/contest.py:132 msgid "freeze after" msgstr "đóng băng sau" -#: judge/models/contest.py:152 +#: judge/models/contest.py:136 msgid "" "Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, " "enter 02:00:00" @@ -822,12 +778,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:156 judge/models/course.py:27 -#: judge/models/course.py:167 judge/models/problem.py:225 +#: judge/models/contest.py:140 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:159 +#: judge/models/contest.py:143 msgid "" "Should be set even for organization-private contests, where it determines " "whether the contest is visible to members of the specified organizations." @@ -835,92 +791,84 @@ msgstr "" "Đánh dấu ngay cả với các kỳ thi riêng tư của nhóm, quyết định việc kỳ thi có " "được hiển thị với các thành viên hay không." -#: judge/models/contest.py:165 +#: judge/models/contest.py:149 msgid "contest rated" msgstr "kỳ thi được xếp hạng" -#: judge/models/contest.py:166 +#: judge/models/contest.py:150 msgid "Whether this contest can be rated." msgstr "Quyết định kỳ thi có được xếp hạng không." -#: judge/models/contest.py:170 +#: judge/models/contest.py:154 msgid "scoreboard visibility" msgstr "khả năng hiển thị của bảng điểm" -#: judge/models/contest.py:173 +#: judge/models/contest.py:157 msgid "Scoreboard visibility through the duration of the contest" msgstr "Khả năng hiển thị của bảng điểm trong thời gian kỳ thi" -#: judge/models/contest.py:178 +#: judge/models/contest.py:162 msgid "view contest scoreboard" msgstr "xem bảng điểm kỳ thi" -#: judge/models/contest.py:181 +#: judge/models/contest.py:165 msgid "These users will be able to view the scoreboard." msgstr "Những người dùng này được phép xem bảng điểm." -#: judge/models/contest.py:184 -msgid "public scoreboard" -msgstr "công khai bảng điểm" - -#: judge/models/contest.py:185 -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:189 +#: judge/models/contest.py:168 msgid "no comments" msgstr "không bình luận" -#: judge/models/contest.py:190 +#: judge/models/contest.py:169 msgid "Use clarification system instead of comments." msgstr "Dùng hệ thống thông báo thay vì bình luận." -#: judge/models/contest.py:195 +#: judge/models/contest.py:174 msgid "Rating floor for contest" msgstr "Cận dưới rating được xếp hạng trong kỳ thi" -#: judge/models/contest.py:201 +#: judge/models/contest.py:180 msgid "Rating ceiling for contest" msgstr "Cận trên rating được xếp hạng trong kỳ thi" -#: judge/models/contest.py:206 +#: judge/models/contest.py:185 msgid "rate all" msgstr "xếp hạng tất cả" -#: judge/models/contest.py:207 +#: judge/models/contest.py:186 msgid "Rate all users who joined." msgstr "Xếp hạng tất cả người dùng đã tham gia (kể cả không nộp)." -#: judge/models/contest.py:212 +#: judge/models/contest.py:191 msgid "exclude from ratings" msgstr "không xếp hạng" -#: judge/models/contest.py:217 +#: judge/models/contest.py:196 msgid "private to specific users" msgstr "riêng tư với các người dùng này" -#: judge/models/contest.py:222 +#: judge/models/contest.py:201 msgid "private contestants" msgstr "thí sinh riêng tư" -#: judge/models/contest.py:223 +#: judge/models/contest.py:202 msgid "If private, only these users may see the contest" msgstr "Nếu riêng tư, chỉ những người dùng này mới thấy kỳ thi" -#: judge/models/contest.py:227 +#: judge/models/contest.py:206 msgid "hide problem tags" msgstr "ẩn nhãn kỳ thi" -#: judge/models/contest.py:228 +#: judge/models/contest.py:207 msgid "Whether problem tags should be hidden by default." msgstr "" "Quyết định việc nhãn bài tập (DP, Tham lam, ...) được ẩn trong kỳ thi không." -#: judge/models/contest.py:232 +#: judge/models/contest.py:211 msgid "run pretests only" msgstr "chỉ chạy pretests" -#: judge/models/contest.py:234 +#: judge/models/contest.py:213 msgid "" "Whether judges should grade pretests only, versus all testcases. Commonly " "set during a contest, then unset prior to rejudging user submissions when " @@ -929,57 +877,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:241 judge/models/interface.py:97 -#: judge/models/problem.py:285 +#: judge/models/contest.py:220 judge/models/interface.py:92 +#: judge/models/problem.py:279 msgid "private to organizations" msgstr "riêng tư với các tổ chức" -#: judge/models/contest.py:246 judge/models/course.py:33 -#: judge/models/interface.py:93 judge/models/problem.py:281 -#: judge/models/profile.py:167 +#: judge/models/contest.py:225 judge/models/course.py:34 +#: judge/models/interface.py:88 judge/models/problem.py:275 +#: judge/models/profile.py:130 msgid "organizations" msgstr "tổ chức" -#: judge/models/contest.py:247 +#: judge/models/contest.py:226 msgid "If private, only these organizations may see the contest" msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được kỳ thi" -#: judge/models/contest.py:250 -#, fuzzy -#| msgid "contest name" -msgid "contest in course" -msgstr "tên kỳ thi" - -#: judge/models/contest.py:254 judge/models/problem.py:256 +#: judge/models/contest.py:229 judge/models/problem.py:253 msgid "OpenGraph image" msgstr "Hình ảnh OpenGraph" -#: judge/models/contest.py:257 +#: judge/models/contest.py:232 judge/models/profile.py:85 msgid "Logo override image" msgstr "Hình ảnh ghi đè logo" -#: judge/models/contest.py:262 +#: judge/models/contest.py:237 msgid "" "This image will replace the default site logo for users inside the contest." msgstr "Ảnh này sẽ thay thế cho logo mặc định trong kỳ thi." -#: judge/models/contest.py:270 +#: judge/models/contest.py:245 msgid "the amount of live participants" msgstr "số lượng thí sinh thi trực tiếp" -#: judge/models/contest.py:274 +#: judge/models/contest.py:249 msgid "contest summary" msgstr "tổng kết kỳ thi" -#: judge/models/contest.py:276 judge/models/problem.py:262 +#: judge/models/contest.py:251 judge/models/problem.py:259 msgid "Plain-text, shown in meta description tag, e.g. for social media." msgstr "" -#: judge/models/contest.py:280 judge/models/profile.py:109 +#: judge/models/contest.py:255 judge/models/profile.py:80 msgid "access code" msgstr "mật khẩu truy cập" -#: judge/models/contest.py:285 +#: judge/models/contest.py:260 msgid "" "An optional code to prompt contestants before they are allowed to join the " "contest. Leave it blank to disable." @@ -987,488 +929,453 @@ 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:291 judge/models/problem.py:244 +#: judge/models/contest.py:266 judge/models/problem.py:241 msgid "personae non gratae" msgstr "Chặn tham gia" -#: judge/models/contest.py:293 +#: judge/models/contest.py:268 msgid "Bans the selected users from joining this contest." msgstr "Cấm những người dùng được chọn tham gia kỳ thi." -#: judge/models/contest.py:296 +#: judge/models/contest.py:271 msgid "contest format" msgstr "format kỳ thi" -#: judge/models/contest.py:300 +#: judge/models/contest.py:275 msgid "The contest format module to use." msgstr "Format kỳ thi sử dụng." -#: judge/models/contest.py:303 +#: judge/models/contest.py:278 msgid "contest format configuration" msgstr "Tùy chỉnh format kỳ thi" -#: judge/models/contest.py:307 +#: judge/models/contest.py:282 msgid "" "A JSON object to serve as the configuration for the chosen contest format " "module. Leave empty to use None. Exact format depends on the contest format " "selected." msgstr "" -#: judge/models/contest.py:320 +#: judge/models/contest.py:295 msgid "precision points" msgstr "Hiển thị điểm" -#: judge/models/contest.py:323 +#: judge/models/contest.py:298 msgid "Number of digits to round points to." msgstr "Số chữ số thập phân trên bảng điểm." -#: judge/models/contest.py:326 -msgid "rate limit" -msgstr "giới hạn bài nộp" - -#: judge/models/contest.py:331 -msgid "" -"Maximum number of submissions per minute. Leave empty if you don't want rate " -"limit." -msgstr "Số bài nộp tối đa mỗi phút. Để trống nếu không muốn giới hạn." - -#: judge/models/contest.py:362 -msgid "End time must be after start time" -msgstr "Thời gian kết thúc phải sau thời gian bắt đầu" - -#: judge/models/contest.py:681 +#: judge/models/contest.py:606 msgid "See private contests" msgstr "" -#: judge/models/contest.py:682 +#: judge/models/contest.py:607 msgid "Edit own contests" msgstr "" -#: judge/models/contest.py:683 +#: judge/models/contest.py:608 msgid "Edit all contests" msgstr "" -#: judge/models/contest.py:684 +#: judge/models/contest.py:609 msgid "Clone contest" msgstr "" -#: judge/models/contest.py:685 templates/contest/moss.html:72 +#: judge/models/contest.py:610 templates/contest/moss.html:72 msgid "MOSS contest" msgstr "" -#: judge/models/contest.py:686 +#: judge/models/contest.py:611 msgid "Rate contests" msgstr "" -#: judge/models/contest.py:687 +#: judge/models/contest.py:612 msgid "Contest access codes" msgstr "" -#: judge/models/contest.py:688 +#: judge/models/contest.py:613 msgid "Create private contests" msgstr "" -#: judge/models/contest.py:689 +#: judge/models/contest.py:614 msgid "Change contest visibility" msgstr "" -#: judge/models/contest.py:690 +#: judge/models/contest.py:615 msgid "Edit contest problem label script" msgstr "Cách hiển thị thứ tự bài tập" -#: judge/models/contest.py:692 judge/models/contest.py:853 -#: judge/models/contest.py:931 judge/models/contest.py:961 -#: judge/models/contest.py:1040 judge/models/submission.py:116 +#: judge/models/contest.py:617 judge/models/contest.py:764 +#: judge/models/contest.py:845 judge/models/contest.py:875 +#: judge/models/course.py:178 judge/models/submission.py:116 msgid "contest" msgstr "kỳ thi" -#: judge/models/contest.py:693 +#: judge/models/contest.py:618 msgid "contests" msgstr "kỳ thi" -#: judge/models/contest.py:716 +#: judge/models/contest.py:627 msgid "associated contest" msgstr "" -#: judge/models/contest.py:729 judge/models/course.py:185 +#: judge/models/contest.py:640 msgid "score" msgstr "điểm" -#: judge/models/contest.py:730 +#: judge/models/contest.py:641 msgid "cumulative time" msgstr "tổng thời gian" -#: judge/models/contest.py:732 +#: judge/models/contest.py:643 msgid "is disqualified" msgstr "đã bị loại" -#: judge/models/contest.py:734 +#: judge/models/contest.py:645 msgid "Whether this participation is disqualified." msgstr "Quyết định thí sinh có bị loại không." -#: judge/models/contest.py:736 +#: judge/models/contest.py:647 msgid "tie-breaking field" msgstr "" -#: judge/models/contest.py:738 +#: judge/models/contest.py:649 msgid "virtual participation id" msgstr "id lần tham gia ảo" -#: judge/models/contest.py:740 +#: judge/models/contest.py:651 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:743 +#: judge/models/contest.py:654 msgid "contest format specific data" msgstr "" -#: judge/models/contest.py:746 +#: judge/models/contest.py:657 msgid "same as format_data, but including frozen results" msgstr "" -#: judge/models/contest.py:750 +#: judge/models/contest.py:661 +#, fuzzy +#| msgid "score" msgid "final score" msgstr "điểm" -#: judge/models/contest.py:752 +#: judge/models/contest.py:663 +#, fuzzy +#| msgid "cumulative time" msgid "final cumulative time" msgstr "tổng thời gian" -#: judge/models/contest.py:828 +#: judge/models/contest.py:739 #, python-format msgid "%s spectating in %s" msgstr "%s đang theo dõi trong %s" -#: judge/models/contest.py:833 +#: judge/models/contest.py:744 #, python-format msgid "%s in %s, v%d" msgstr "%s trong %s, v%d" -#: judge/models/contest.py:838 +#: judge/models/contest.py:749 #, python-format msgid "%s in %s" msgstr "%s trong %s" -#: judge/models/contest.py:841 +#: judge/models/contest.py:752 msgid "contest participation" msgstr "lần tham gia kỳ thi" -#: judge/models/contest.py:842 +#: judge/models/contest.py:753 msgid "contest participations" msgstr "lần tham gia kỳ thi" -#: judge/models/contest.py:849 judge/models/contest.py:902 -#: judge/models/contest.py:964 judge/models/problem.py:609 -#: judge/models/problem.py:616 judge/models/problem.py:637 -#: judge/models/problem.py:668 judge/models/problem_data.py:50 +#: judge/models/contest.py:760 judge/models/contest.py:816 +#: judge/models/contest.py:878 judge/models/problem.py:558 +#: judge/models/problem.py:565 judge/models/problem.py:586 +#: judge/models/problem.py:617 judge/models/problem_data.py:50 msgid "problem" msgstr "bài tập" -#: judge/models/contest.py:857 judge/models/contest.py:914 -#: judge/models/course.py:166 judge/models/course.py:199 -#: judge/models/problem.py:209 +#: judge/models/contest.py:768 judge/models/contest.py:828 +#: judge/models/course.py:182 judge/models/problem.py:206 msgid "points" msgstr "điểm" -#: judge/models/contest.py:858 +#: judge/models/contest.py:769 msgid "partial" msgstr "thành phần" -#: judge/models/contest.py:859 judge/models/contest.py:916 +#: judge/models/contest.py:770 judge/models/contest.py:830 msgid "is pretested" msgstr "dùng pretest" -#: judge/models/contest.py:860 judge/models/course.py:165 -#: judge/models/course.py:184 judge/models/course.py:198 -#: judge/models/interface.py:48 +#: judge/models/contest.py:771 judge/models/interface.py:43 msgid "order" msgstr "thứ tự" -#: judge/models/contest.py:862 +#: judge/models/contest.py:773 +msgid "0 to not show testcases, 1 to show" +msgstr "0 để ẩn test, 1 để hiện" + +#: judge/models/contest.py:774 msgid "visible testcases" msgstr "hiển thị test" -#: judge/models/contest.py:867 +#: judge/models/contest.py:781 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:869 +#: judge/models/contest.py:783 msgid "max submissions" msgstr "số lần nộp tối đa" -#: judge/models/contest.py:872 +#: judge/models/contest.py:786 msgid "Why include a problem you can't submit to?" msgstr "" -#: judge/models/contest.py:876 +#: judge/models/contest.py:790 #, 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:877 +#: judge/models/contest.py:791 +#, fuzzy +#| msgid "frozen subtasks" msgid "hidden subtasks" msgstr "Đóng băng subtasks" -#: judge/models/contest.py:889 +#: judge/models/contest.py:803 msgid "contest problem" msgstr "bài trong kỳ thi" -#: judge/models/contest.py:890 +#: judge/models/contest.py:804 msgid "contest problems" msgstr "bài trong kỳ thi" -#: judge/models/contest.py:896 judge/models/submission.py:274 +#: judge/models/contest.py:810 judge/models/submission.py:233 msgid "submission" msgstr "bài nộp" -#: judge/models/contest.py:909 judge/models/contest.py:935 +#: judge/models/contest.py:823 judge/models/contest.py:849 msgid "participation" msgstr "lần tham gia" -#: judge/models/contest.py:917 +#: judge/models/contest.py:831 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:922 +#: judge/models/contest.py:836 msgid "contest submission" msgstr "bài nộp kỳ thi" -#: judge/models/contest.py:923 +#: judge/models/contest.py:837 msgid "contest submissions" msgstr "bài nộp kỳ thi" -#: judge/models/contest.py:939 +#: judge/models/contest.py:853 msgid "rank" msgstr "rank" -#: judge/models/contest.py:940 +#: judge/models/contest.py:854 msgid "rating" msgstr "rating" -#: judge/models/contest.py:941 +#: judge/models/contest.py:855 msgid "raw rating" msgstr "rating thật" -#: judge/models/contest.py:942 +#: judge/models/contest.py:856 msgid "contest performance" msgstr "" -#: judge/models/contest.py:943 +#: judge/models/contest.py:857 msgid "last rated" msgstr "lần cuối được xếp hạng" -#: judge/models/contest.py:947 +#: judge/models/contest.py:861 msgid "contest rating" msgstr "rating kỳ thi" -#: judge/models/contest.py:948 +#: judge/models/contest.py:862 msgid "contest ratings" msgstr "rating kỳ thi" -#: judge/models/contest.py:972 +#: judge/models/contest.py:886 msgid "contest moss result" msgstr "kết quả MOSS kỳ thi" -#: judge/models/contest.py:973 +#: judge/models/contest.py:887 msgid "contest moss results" msgstr "kết quả MOSS kỳ thi" -#: judge/models/contest.py:978 +#: judge/models/contest.py:892 msgid "clarified problem" msgstr "" -#: judge/models/contest.py:980 +#: judge/models/contest.py:894 msgid "clarification body" msgstr "" -#: judge/models/contest.py:982 +#: judge/models/contest.py:896 msgid "clarification timestamp" msgstr "" -#: judge/models/contest.py:1001 -msgid "contests summary" -msgstr "tổng kết kỳ thi" - -#: judge/models/contest.py:1002 -msgid "contests summaries" -msgstr "tổng kết kỳ thi" - -#: judge/models/contest.py:1013 judge/models/contest.py:1020 -msgid "official contest category" -msgstr "loại kỳ thi chính thức" - -#: judge/models/contest.py:1021 -msgid "official contest categories" -msgstr "các loại kỳ thi chính thức" - -#: judge/models/contest.py:1026 judge/models/contest.py:1033 -msgid "official contest location" -msgstr "địa điểm kỳ thi chính thức" - -#: judge/models/contest.py:1034 -msgid "official contest locations" -msgstr "các địa điểm kỳ thi chính thức" - -#: judge/models/contest.py:1046 -msgid "contest category" -msgstr "loại kỳ thi" - -#: judge/models/contest.py:1049 -msgid "year" -msgstr "năm" - -#: judge/models/contest.py:1052 -msgid "contest location" -msgstr "địa điểm kỳ thi" - -#: judge/models/contest.py:1057 -msgid "official contest" -msgstr "kỳ thi chính thức" - -#: judge/models/contest.py:1058 -msgid "official contests" -msgstr "các kỳ thi chính thức" - -#: judge/models/course.py:12 templates/course/grades.html:88 -#: templates/course/grades_lesson.html:88 -msgid "Student" -msgstr "Học sinh" - -#: judge/models/course.py:13 -msgid "Assistant" -msgstr "Trợ giảng" - -#: judge/models/course.py:14 -msgid "Teacher" -msgstr "Giáo viên" - -#: judge/models/course.py:23 +#: judge/models/course.py:21 +#, fuzzy +#| msgid "username" msgid "course name" -msgstr "tên khóa học" +msgstr "tên đăng nhập" + +#: judge/models/course.py:23 judge/models/profile.py:46 +msgid "organization description" +msgstr "mô tả tổ chức" #: judge/models/course.py:25 -msgid "course description" -msgstr "Mô tả khóa học" +#, fuzzy +#| msgid "end time" +msgid "ending time" +msgstr "thời gian kết thúc" -#: judge/models/course.py:34 +#: judge/models/course.py:35 +#, fuzzy +#| msgid "If private, only these organizations may see the contest" msgid "If private, only these organizations may see the course" -msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được khóa học" - -#: judge/models/course.py:38 -msgid "course slug" -msgstr "url khóa học" +msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được kỳ thi" #: judge/models/course.py:39 +msgid "course slug" +msgstr "" + +#: judge/models/course.py:40 +#, fuzzy +#| msgid "Organization name shown in URL" msgid "Course name shown in URL" msgstr "Tên được hiển thị trong đường dẫn" -#: judge/models/course.py:42 judge/models/profile.py:65 +#: judge/models/course.py:43 judge/models/profile.py:38 msgid "Only alphanumeric and hyphens" -msgstr "Chỉ chứa chữ cái và dấu gạch ngang (-)" +msgstr "" -#: judge/models/course.py:46 +#: judge/models/course.py:47 +#, fuzzy +#| msgid "Registration" msgid "public registration" -msgstr "Cho phép đăng ký" +msgstr "Đăng ký" -#: judge/models/course.py:50 +#: judge/models/course.py:51 msgid "course image" -msgstr "hình ảnh khóa học" +msgstr "" -#: judge/models/course.py:123 judge/models/course.py:159 +#: judge/models/course.py:109 judge/models/course.py:147 +#: judge/models/course.py:172 msgid "course" -msgstr "khóa học" +msgstr "" -#: judge/models/course.py:163 +#: judge/models/course.py:117 +msgid "user_of_course" +msgstr "" + +#: judge/models/course.py:121 +msgid "Student" +msgstr "" + +#: judge/models/course.py:122 +msgid "Assistant" +msgstr "" + +#: judge/models/course.py:123 +msgid "Teacher" +msgstr "" + +#: judge/models/course.py:152 #, fuzzy -#| msgid "message title" -msgid "lesson title" -msgstr "tiêu đề tin nhắn" +#| msgid "user profiles" +msgid "course files" +msgstr "thông tin người dùng" -#: judge/models/course.py:164 -#, fuzzy -#| msgid "post content" -msgid "lesson content" -msgstr "đăng nội dung" - -#: judge/models/interface.py:29 +#: judge/models/interface.py:24 msgid "configuration item" msgstr "" -#: judge/models/interface.py:30 +#: judge/models/interface.py:25 msgid "miscellaneous configuration" msgstr "" -#: judge/models/interface.py:42 +#: judge/models/interface.py:37 msgid "navigation item" msgstr "mục điều hướng" -#: judge/models/interface.py:43 +#: judge/models/interface.py:38 msgid "navigation bar" msgstr "thanh điều hướng" -#: judge/models/interface.py:49 +#: judge/models/interface.py:44 msgid "identifier" msgstr "" -#: judge/models/interface.py:50 +#: judge/models/interface.py:45 msgid "label" msgstr "nhãn" -#: judge/models/interface.py:53 +#: judge/models/interface.py:48 msgid "highlight regex" msgstr "" -#: judge/models/interface.py:57 +#: judge/models/interface.py:52 msgid "parent item" msgstr "mục cha" -#: judge/models/interface.py:79 +#: judge/models/interface.py:74 msgid "post title" msgstr "tiêu đề bài đăng" -#: judge/models/interface.py:81 +#: judge/models/interface.py:75 judge/models/problem.py:643 +msgid "authors" +msgstr "tác giả" + +#: judge/models/interface.py:76 msgid "slug" msgstr "slug" -#: judge/models/interface.py:82 judge/models/problem.py:692 +#: judge/models/interface.py:77 judge/models/problem.py:641 msgid "public visibility" msgstr "khả năng hiển thị công khai" -#: judge/models/interface.py:83 +#: judge/models/interface.py:78 msgid "sticky" msgstr "nổi lên đầu" -#: judge/models/interface.py:84 +#: judge/models/interface.py:79 msgid "publish after" msgstr "đăng sau khi" -#: judge/models/interface.py:85 +#: judge/models/interface.py:80 msgid "post content" msgstr "đăng nội dung" -#: judge/models/interface.py:86 +#: judge/models/interface.py:81 msgid "post summary" msgstr "đăng tổng kết" -#: judge/models/interface.py:88 +#: judge/models/interface.py:83 msgid "openGraph image" msgstr "hình ảnh openGraph" -#: judge/models/interface.py:94 +#: judge/models/interface.py:89 msgid "If private, only these organizations may see the blog post." msgstr "Nếu riêng tư, chỉ những tổ chức này thấy được bài đăng." -#: judge/models/interface.py:141 +#: judge/models/interface.py:129 msgid "Edit all posts" msgstr "Chỉnh sửa tất cả bài đăng" -#: judge/models/interface.py:142 +#: judge/models/interface.py:130 msgid "blog post" msgstr "bài đăng" -#: judge/models/interface.py:143 +#: judge/models/interface.py:131 msgid "blog posts" msgstr "bài đăng" @@ -1492,236 +1399,168 @@ msgstr "người nhận" msgid "message timestamp" msgstr "thời gian gửi" -#: judge/models/message.py:28 -msgid "read" -msgstr "" - #: judge/models/message.py:33 msgid "messages in the thread" msgstr "tin nhắn trong chuỗi" -#: judge/models/notification.py:11 -msgid "Added a post" -msgstr "Thêm bài đăng" - -#: judge/models/notification.py:12 -msgid "You are added to a group" -msgstr "Bạn đã được thêm vào nhóm." - -#: judge/models/notification.py:13 -msgid "You have a new comment" -msgstr "Bạn có bình luận mới" - -#: judge/models/notification.py:14 -msgid "Deleted a post" -msgstr "Đã xóa bài đăng" - -#: judge/models/notification.py:15 -msgid "Rejected a post" -msgstr "Đã từ chối bài đăng" - -#: judge/models/notification.py:16 -msgid "Approved a post" -msgstr "Đã chấp thuận bài đăng" - -#: judge/models/notification.py:17 -msgid "Edited a post" -msgstr "Đã chỉnh sửa bài đăng" - -#: judge/models/notification.py:18 -msgid "Mentioned you" -msgstr "Đã nhắc đến bạn" - -#: judge/models/notification.py:19 -msgid "Replied you" -msgstr "Đã phản hồi bạn" - -#: judge/models/notification.py:20 templates/blog/list.html:47 -msgid "Ticket" -msgstr "Báo cáo" - -#: judge/models/notification.py:27 -msgid "owner" -msgstr "" - -#: judge/models/notification.py:32 -msgid "category" -msgstr "" - -#: judge/models/notification.py:35 -msgid "html link to comments, used for non-comments" -msgstr "" - -#: judge/models/notification.py:41 -msgid "who trigger, used for non-comment" -msgstr "" - -#: judge/models/notification.py:54 -msgid "The problem is public to: " -msgstr "Bài tập được công khai trong: " - -#: judge/models/notification.py:56 -msgid "The problem is private to: " -msgstr "Bài tập riêng tư trong: " - -#: judge/models/notification.py:59 -msgid "The problem is public to everyone." -msgstr "Bài tập được công khai với mọi người." - -#: judge/models/notification.py:61 -msgid "The problem is private." -msgstr "Bài tập được đánh dấu riêng tư." - -#: judge/models/pagevote.py:25 +#: judge/models/pagevote.py:19 #, fuzzy #| msgid "votes" msgid "pagevote" msgstr "bình chọn" -#: judge/models/pagevote.py:26 +#: judge/models/pagevote.py:20 #, fuzzy #| msgid "votes" msgid "pagevotes" msgstr "bình chọn" -#: judge/models/pagevote.py:48 +#: judge/models/pagevote.py:40 #, fuzzy #| msgid "volunteer vote" msgid "pagevote vote" msgstr "vote từ TNV" -#: judge/models/pagevote.py:49 +#: judge/models/pagevote.py:41 #, fuzzy #| msgid "volunteer votes" msgid "pagevote votes" msgstr "vote từ TNV" -#: judge/models/problem.py:46 +#: judge/models/problem.py:41 msgid "problem category ID" msgstr "mã của nhóm bài" -#: judge/models/problem.py:49 +#: judge/models/problem.py:44 msgid "problem category name" msgstr "tên nhóm bài" -#: judge/models/problem.py:57 +#: judge/models/problem.py:52 msgid "problem type" msgstr "dạng bài" -#: judge/models/problem.py:58 judge/models/problem.py:176 +#: judge/models/problem.py:53 judge/models/problem.py:173 #: judge/models/volunteer.py:28 msgid "problem types" msgstr "dạng bài" -#: judge/models/problem.py:63 +#: judge/models/problem.py:58 msgid "problem group ID" msgstr "mã của nhóm bài" -#: judge/models/problem.py:65 +#: judge/models/problem.py:60 msgid "problem group name" msgstr "tên nhóm bài" -#: judge/models/problem.py:72 judge/models/problem.py:181 +#: judge/models/problem.py:67 judge/models/problem.py:178 msgid "problem group" msgstr "nhóm bài" -#: judge/models/problem.py:73 +#: judge/models/problem.py:68 msgid "problem groups" msgstr "nhóm bài" -#: judge/models/problem.py:80 +#: judge/models/problem.py:75 msgid "key" msgstr "" -#: judge/models/problem.py:83 +#: judge/models/problem.py:78 msgid "link" msgstr "đường dẫn" -#: judge/models/problem.py:84 +#: judge/models/problem.py:79 msgid "full name" msgstr "tên đầy đủ" -#: judge/models/problem.py:88 judge/models/profile.py:70 -#: judge/models/runtime.py:35 +#: judge/models/problem.py:83 judge/models/profile.py:43 +#: judge/models/runtime.py:34 msgid "short name" msgstr "tên ngắn" -#: judge/models/problem.py:89 +#: judge/models/problem.py:84 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:94 +#: judge/models/problem.py:89 msgid "icon" msgstr "icon" -#: judge/models/problem.py:95 +#: judge/models/problem.py:90 msgid "URL to the icon" msgstr "Đường dẫn icon" -#: judge/models/problem.py:97 +#: judge/models/problem.py:92 msgid "license text" msgstr "văn bản giấy phép" -#: judge/models/problem.py:106 +#: judge/models/problem.py:101 msgid "license" msgstr "" -#: judge/models/problem.py:107 +#: judge/models/problem.py:102 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:161 +#: judge/models/problem.py:154 +msgid "curators" +msgstr "" + +#: 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:171 +#: judge/models/problem.py:164 +msgid "testers" +msgstr "" + +#: 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." @@ -1729,11 +1568,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:652 +#: judge/models/problem.py:194 judge/models/problem.py:601 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)." @@ -1741,7 +1580,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." @@ -1749,148 +1588,143 @@ 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:276 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:282 msgid "pdf statement" msgstr "Đề bài bằng file pdf" -#: judge/models/problem.py:621 judge/models/problem.py:642 -#: judge/models/problem.py:673 judge/models/runtime.py:159 +#: judge/models/problem.py:570 judge/models/problem.py:591 +#: judge/models/problem.py:622 judge/models/runtime.py:161 msgid "language" msgstr "" -#: judge/models/problem.py:624 +#: judge/models/problem.py:573 msgid "translated name" msgstr "" -#: judge/models/problem.py:626 +#: judge/models/problem.py:575 msgid "translated description" msgstr "" -#: judge/models/problem.py:630 +#: judge/models/problem.py:579 msgid "problem translation" msgstr "" -#: judge/models/problem.py:631 +#: judge/models/problem.py:580 msgid "problem translations" msgstr "" -#: judge/models/problem.py:661 +#: judge/models/problem.py:610 msgid "language-specific resource limit" msgstr "" -#: judge/models/problem.py:662 +#: judge/models/problem.py:611 msgid "language-specific resource limits" msgstr "" -#: judge/models/problem.py:675 judge/models/submission.py:291 +#: judge/models/problem.py:624 judge/models/submission.py:244 msgid "source code" msgstr "mã nguồn" -#: judge/models/problem.py:679 +#: judge/models/problem.py:628 msgid "language-specific template" msgstr "" -#: judge/models/problem.py:680 +#: judge/models/problem.py:629 msgid "language-specific templates" msgstr "" -#: judge/models/problem.py:687 +#: judge/models/problem.py:636 msgid "associated problem" msgstr "" -#: judge/models/problem.py:693 +#: judge/models/problem.py:642 msgid "publish date" msgstr "" -#: judge/models/problem.py:695 +#: judge/models/problem.py:644 msgid "editorial content" msgstr "nội dung lời giải" -#: judge/models/problem.py:712 -#, python-format -msgid "Editorial for %s" -msgstr "" - -#: judge/models/problem.py:716 +#: judge/models/problem.py:658 msgid "solution" msgstr "lời giải" -#: judge/models/problem.py:717 +#: judge/models/problem.py:659 msgid "solutions" msgstr "lời giải" -#: judge/models/problem.py:722 +#: judge/models/problem.py:664 #, fuzzy #| msgid "point value" msgid "proposed point value" msgstr "điểm" -#: judge/models/problem.py:723 +#: judge/models/problem.py:665 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:737 +#: judge/models/problem.py:679 msgid "The time this vote was cast" msgstr "" -#: judge/models/problem.py:743 +#: judge/models/problem.py:685 msgid "vote" msgstr "" @@ -1912,7 +1746,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 xét dấu cách cuối dòng" +msgstr "Không cho phép dấu cách cuối dòng" #: judge/models/problem_data.py:37 msgid "Unordered" @@ -1931,8 +1765,8 @@ msgid "Custom checker (PY)" msgstr "Trình chấm tự viết (Python)" #: judge/models/problem_data.py:41 -msgid "Custom checker (CPP)" -msgstr "Trình chấm tự viết (CPP)" +msgid "Custom validator (CPP)" +msgstr "Trình chấm tự viết (C++)" #: judge/models/problem_data.py:42 msgid "Interactive" @@ -1950,11 +1784,11 @@ msgstr "file zip chứa test" msgid "generator file" msgstr "file tạo test" -#: judge/models/problem_data.py:69 judge/models/problem_data.py:238 +#: judge/models/problem_data.py:69 judge/models/problem_data.py:205 msgid "output prefix length" msgstr "độ dài hiển thị output" -#: judge/models/problem_data.py:72 judge/models/problem_data.py:241 +#: judge/models/problem_data.py:72 judge/models/problem_data.py:208 msgid "output limit length" msgstr "giới hạn hiển thị output" @@ -1962,31 +1796,31 @@ msgstr "giới hạn hiển thị output" msgid "init.yml generation feedback" msgstr "phản hồi của quá trình tạo file init.yml" -#: judge/models/problem_data.py:78 judge/models/problem_data.py:244 +#: judge/models/problem_data.py:78 judge/models/problem_data.py:211 msgid "checker" msgstr "trình chấm" -#: judge/models/problem_data.py:81 judge/models/problem_data.py:247 +#: judge/models/problem_data.py:81 judge/models/problem_data.py:214 msgid "checker arguments" msgstr "các biến trong trình chấm" -#: judge/models/problem_data.py:83 judge/models/problem_data.py:249 +#: judge/models/problem_data.py:83 judge/models/problem_data.py:216 msgid "checker arguments as a JSON object" msgstr "các biến trong trình chấm theo dạng JSON" #: judge/models/problem_data.py:86 msgid "custom checker file" -msgstr "trình chấm" +msgstr "file trình chấm" #: judge/models/problem_data.py:94 -msgid "custom cpp checker file" -msgstr "trình chấm C++" +msgid "custom validator file" +msgstr "file trình chấm" #: judge/models/problem_data.py:102 msgid "interactive judge" -msgstr "trình chấm interactive" +msgstr "" -#: judge/models/problem_data.py:110 judge/models/problem_data.py:229 +#: judge/models/problem_data.py:110 judge/models/problem_data.py:196 msgid "input file name" msgstr "tên file input" @@ -1994,7 +1828,7 @@ msgstr "tên file input" msgid "Leave empty for stdin" msgstr "Để trống nếu nhập từ bàn phím" -#: judge/models/problem_data.py:116 judge/models/problem_data.py:232 +#: judge/models/problem_data.py:116 judge/models/problem_data.py:199 msgid "output file name" msgstr "tên file output" @@ -2002,448 +1836,422 @@ msgstr "tên file output" msgid "Leave empty for stdout" msgstr "Để trống nếu xuất ra màn hình" -#: judge/models/problem_data.py:122 -msgid "is output only" -msgstr "Output-only?" - -#: judge/models/problem_data.py:123 -msgid "Support output-only problem" -msgstr "Dùng cho các bài output-only (nộp bằng file zip)" - -#: judge/models/problem_data.py:127 -msgid "is IOI signature" -msgstr "Nộp bài bằng hàm?" - -#: judge/models/problem_data.py:128 -msgid "Use IOI Signature" -msgstr "Nộp bài bằng hàm như IOI" - -#: judge/models/problem_data.py:132 -msgid "signature handler" -msgstr "File xử lý hàm" - -#: judge/models/problem_data.py:140 -msgid "signature header" -msgstr "File định nghĩa hàm" - -#: judge/models/problem_data.py:213 +#: judge/models/problem_data.py:180 msgid "problem data set" msgstr "tập hợp dữ liệu bài" -#: judge/models/problem_data.py:217 +#: judge/models/problem_data.py:184 msgid "case position" msgstr "vị trí test" -#: judge/models/problem_data.py:220 +#: judge/models/problem_data.py:187 msgid "case type" msgstr "loại test" -#: judge/models/problem_data.py:222 +#: judge/models/problem_data.py:189 msgid "Normal case" msgstr "Test bình thường" -#: judge/models/problem_data.py:223 +#: judge/models/problem_data.py:190 msgid "Batch start" msgstr "Bắt đầu nhóm" -#: judge/models/problem_data.py:224 +#: judge/models/problem_data.py:191 msgid "Batch end" msgstr "Kết thúc nhóm" -#: judge/models/problem_data.py:234 +#: judge/models/problem_data.py:201 msgid "generator arguments" msgstr "biến trong file sinh test" -#: judge/models/problem_data.py:235 +#: judge/models/problem_data.py:202 msgid "point value" msgstr "điểm" -#: judge/models/problem_data.py:236 +#: judge/models/problem_data.py:203 msgid "case is pretest?" msgstr "test là pretest?" -#: judge/models/profile.py:58 +#: judge/models/profile.py:31 msgid "organization title" msgstr "tiêu đề tổ chức" -#: judge/models/profile.py:61 +#: judge/models/profile.py:34 msgid "organization slug" msgstr "tên ngắn đường dẫn" -#: judge/models/profile.py:62 +#: judge/models/profile.py:35 msgid "Organization name shown in URL" msgstr "Tên được hiển thị trong đường dẫn" -#: judge/models/profile.py:71 +#: judge/models/profile.py:44 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:74 -msgid "organization description" -msgstr "mô tả tổ chức" - -#: judge/models/profile.py:78 +#: judge/models/profile.py:49 msgid "registrant" msgstr "người tạo" -#: judge/models/profile.py:81 +#: judge/models/profile.py:52 msgid "User who registered this organization" msgstr "Người tạo tổ chức" -#: judge/models/profile.py:85 +#: judge/models/profile.py:56 msgid "administrators" msgstr "người quản lý" -#: judge/models/profile.py:87 +#: judge/models/profile.py:58 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:90 +#: judge/models/profile.py:61 msgid "creation date" msgstr "ngày tạo" -#: judge/models/profile.py:93 +#: judge/models/profile.py:64 msgid "is open organization?" msgstr "tổ chức mở?" -#: judge/models/profile.py:94 +#: judge/models/profile.py:65 msgid "Allow joining organization" msgstr "Cho phép mọi người tham gia tổ chức" -#: judge/models/profile.py:98 +#: judge/models/profile.py:69 msgid "maximum size" msgstr "số lượng thành viên tối đa" -#: judge/models/profile.py:102 +#: judge/models/profile.py:73 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:108 +#: judge/models/profile.py:79 msgid "Student access code" msgstr "Mã truy cập cho học sinh" -#: judge/models/profile.py:166 judge/models/profile.py:198 -#: judge/models/profile.py:471 judge/models/profile.py:546 +#: judge/models/profile.py:90 +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:129 judge/models/profile.py:158 +#: judge/models/profile.py:374 judge/models/profile.py:451 msgid "organization" msgstr "" -#: judge/models/profile.py:173 +#: judge/models/profile.py:135 msgid "user associated" msgstr "" -#: judge/models/profile.py:176 +#: judge/models/profile.py:137 msgid "self-description" msgstr "" -#: judge/models/profile.py:180 +#: judge/models/profile.py:140 msgid "location" msgstr "" -#: judge/models/profile.py:186 +#: judge/models/profile.py:146 msgid "preferred language" msgstr "" -#: judge/models/profile.py:194 +#: judge/models/profile.py:154 msgid "last access time" msgstr "" -#: judge/models/profile.py:195 +#: judge/models/profile.py:155 msgid "last IP" msgstr "" -#: judge/models/profile.py:206 +#: judge/models/profile.py:166 msgid "display rank" msgstr "" -#: judge/models/profile.py:215 +#: judge/models/profile.py:174 msgid "comment mute" msgstr "" -#: judge/models/profile.py:216 +#: judge/models/profile.py:175 msgid "Some users are at their best when silent." msgstr "" -#: judge/models/profile.py:220 +#: judge/models/profile.py:179 msgid "unlisted user" msgstr "" -#: judge/models/profile.py:221 +#: judge/models/profile.py:180 msgid "User will not be ranked." msgstr "" -#: judge/models/profile.py:227 +#: judge/models/profile.py:184 +#, fuzzy +#| msgid "Banned from joining" +msgid "banned from voting" +msgstr "Bị cấm tham gia" + +#: judge/models/profile.py:185 +msgid "User will not be able to vote on problems' point values." +msgstr "" + +#: judge/models/profile.py:190 +msgid "user script" +msgstr "" + +#: judge/models/profile.py:194 +msgid "User-defined JavaScript for site customization." +msgstr "" + +#: judge/models/profile.py:198 msgid "current contest" msgstr "kỳ thi hiện tại" -#: judge/models/profile.py:234 +#: judge/models/profile.py:205 +msgid "math engine" +msgstr "" + +#: judge/models/profile.py:209 +msgid "the rendering engine used to render math" +msgstr "" + +#: judge/models/profile.py:212 msgid "2FA enabled" msgstr "" -#: judge/models/profile.py:236 +#: judge/models/profile.py:214 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:242 +#: judge/models/profile.py:220 msgid "TOTP key" msgstr "mã TOTP" -#: judge/models/profile.py:243 +#: judge/models/profile.py:221 msgid "32 character base32-encoded key for TOTP" msgstr "" -#: judge/models/profile.py:245 +#: judge/models/profile.py:223 msgid "TOTP key must be empty or base32" msgstr "" -#: judge/models/profile.py:249 +#: judge/models/profile.py:227 msgid "internal notes" msgstr "ghi chú nội bộ" -#: judge/models/profile.py:252 +#: judge/models/profile.py:230 msgid "Notes for administrators regarding this user." msgstr "Ghi chú riêng cho quản trị viên." -#: judge/models/profile.py:257 -msgid "Custom background" -msgstr "Background tự chọn" - -#: judge/models/profile.py:260 -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:428 +#: judge/models/profile.py:361 msgid "user profile" msgstr "thông tin người dùng" -#: judge/models/profile.py:429 +#: judge/models/profile.py:362 msgid "user profiles" msgstr "thông tin người dùng" -#: judge/models/profile.py:435 -#, fuzzy -#| msgid "associated page" -msgid "profile associated" -msgstr "trang tương ứng" - -#: judge/models/profile.py:442 -msgid "t-shirt size" -msgstr "" - -#: judge/models/profile.py:447 -#, fuzzy -#| msgid "date of publishing" -msgid "date of birth" -msgstr "Ngày công bố" - -#: judge/models/profile.py:453 -msgid "address" -msgstr "" - -#: judge/models/profile.py:475 +#: judge/models/profile.py:378 msgid "request time" msgstr "thời gian đăng ký" -#: judge/models/profile.py:478 +#: judge/models/profile.py:381 msgid "state" msgstr "trạng thái" -#: judge/models/profile.py:485 +#: judge/models/profile.py:388 msgid "reason" msgstr "lý do" -#: judge/models/profile.py:488 +#: judge/models/profile.py:391 msgid "organization join request" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:489 +#: judge/models/profile.py:392 msgid "organization join requests" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:551 +#: judge/models/profile.py:456 #, fuzzy #| msgid "last seen" msgid "last visit" msgstr "xem lần cuối" -#: judge/models/runtime.py:22 +#: judge/models/runtime.py:21 msgid "short identifier" msgstr "tên ngắn" -#: judge/models/runtime.py:24 +#: judge/models/runtime.py:23 msgid "" "The identifier for this language; the same as its executor id for judges." msgstr "" -#: judge/models/runtime.py:30 +#: judge/models/runtime.py:29 msgid "long name" msgstr "tên dài" -#: judge/models/runtime.py:31 +#: judge/models/runtime.py:30 msgid "Longer name for the language, e.g. \"Python 2\" or \"C++11\"." msgstr "Tên dài, ví dụ \"Python 2\" or \"C++11\"." -#: judge/models/runtime.py:37 +#: judge/models/runtime.py:36 msgid "" "More readable, but short, name to display publicly; e.g. \"PY2\" or \"C+" "+11\". If left blank, it will default to the short identifier." msgstr "" -#: judge/models/runtime.py:46 +#: judge/models/runtime.py:45 msgid "common name" msgstr "" -#: judge/models/runtime.py:48 +#: judge/models/runtime.py:47 msgid "" "Common name for the language. For example, the common name for C++03, C++11, " "and C++14 would be \"C++\"" msgstr "" -#: judge/models/runtime.py:54 +#: judge/models/runtime.py:53 msgid "ace mode name" msgstr "" -#: judge/models/runtime.py:56 +#: judge/models/runtime.py:55 msgid "" "Language ID for Ace.js editor highlighting, appended to \"mode-\" to " "determine the Ace JavaScript file to use, e.g., \"python\"." msgstr "" -#: judge/models/runtime.py:62 +#: judge/models/runtime.py:61 msgid "pygments name" msgstr "" -#: judge/models/runtime.py:63 +#: judge/models/runtime.py:62 msgid "Language ID for Pygments highlighting in source windows." msgstr "" -#: judge/models/runtime.py:66 +#: judge/models/runtime.py:65 msgid "code template" msgstr "" -#: judge/models/runtime.py:67 +#: judge/models/runtime.py:66 msgid "Code template to display in submission editor." msgstr "" -#: judge/models/runtime.py:72 +#: judge/models/runtime.py:71 msgid "runtime info override" msgstr "" -#: judge/models/runtime.py:75 +#: judge/models/runtime.py:74 msgid "" "Do not set this unless you know what you're doing! It will override the " "usually more specific, judge-provided runtime info!" msgstr "" -#: judge/models/runtime.py:80 +#: judge/models/runtime.py:79 msgid "language description" msgstr "" -#: judge/models/runtime.py:82 +#: judge/models/runtime.py:81 msgid "" "Use this field to inform users of quirks with your environment, additional " "restrictions, etc." msgstr "" -#: judge/models/runtime.py:89 +#: judge/models/runtime.py:88 msgid "extension" msgstr "" -#: judge/models/runtime.py:90 +#: judge/models/runtime.py:89 msgid "The extension of source files, e.g., \"py\" or \"cpp\"." msgstr "" -#: judge/models/runtime.py:160 +#: judge/models/runtime.py:162 msgid "languages" msgstr "ngôn ngữ" -#: judge/models/runtime.py:174 +#: judge/models/runtime.py:168 msgid "language to which this runtime belongs" msgstr "" -#: judge/models/runtime.py:178 +#: judge/models/runtime.py:172 msgid "judge on which this runtime exists" msgstr "" -#: judge/models/runtime.py:180 +#: judge/models/runtime.py:174 msgid "runtime name" msgstr "" -#: judge/models/runtime.py:182 +#: judge/models/runtime.py:176 msgid "runtime version" msgstr "" -#: judge/models/runtime.py:185 +#: judge/models/runtime.py:179 msgid "order in which to display this runtime" msgstr "" -#: judge/models/runtime.py:191 +#: judge/models/runtime.py:185 msgid "Server name, hostname-style" msgstr "Tên web" -#: judge/models/runtime.py:194 +#: judge/models/runtime.py:188 msgid "time of creation" msgstr "ngày tạo" -#: judge/models/runtime.py:198 +#: judge/models/runtime.py:192 msgid "A key to authenticate this judge" msgstr "Chìa khóa xác thực" -#: judge/models/runtime.py:199 +#: judge/models/runtime.py:193 msgid "authentication key" msgstr "mã xác thực" -#: judge/models/runtime.py:202 +#: judge/models/runtime.py:196 msgid "block judge" msgstr "chặn máy chấm" -#: judge/models/runtime.py:205 +#: judge/models/runtime.py:199 msgid "" "Whether this judge should be blocked from connecting, even if its key is " "correct." msgstr "Quyết định có chặn máy chấm, ngay cả khi mã xác thực đúng." -#: judge/models/runtime.py:209 +#: judge/models/runtime.py:203 msgid "judge online status" msgstr "trạng thái online của máy chấm" -#: judge/models/runtime.py:210 +#: judge/models/runtime.py:204 msgid "judge start time" msgstr "thời gian khởi đầu máy chấm" -#: judge/models/runtime.py:211 +#: judge/models/runtime.py:205 msgid "response time" msgstr "thời gian trả lời" -#: judge/models/runtime.py:213 +#: judge/models/runtime.py:207 msgid "system load" msgstr "lưu lượng xử lý" -#: judge/models/runtime.py:215 +#: judge/models/runtime.py:209 msgid "Load for the last minute, divided by processors to be fair." msgstr "Lưu lượng được chia đều." -#: judge/models/runtime.py:225 judge/models/runtime.py:267 +#: judge/models/runtime.py:219 judge/models/runtime.py:261 msgid "judges" msgstr "máy chấm" -#: judge/models/runtime.py:266 +#: judge/models/runtime.py:260 msgid "judge" msgstr "máy chấm" #: judge/models/submission.py:20 judge/models/submission.py:47 -#: judge/utils/problems.py:116 +#: judge/utils/problems.py:120 msgid "Accepted" msgstr "Accepted" #: judge/models/submission.py:21 judge/models/submission.py:48 -#: judge/utils/problems.py:119 msgid "Wrong Answer" msgstr "Wrong Answer" #: judge/models/submission.py:22 judge/models/submission.py:50 -#: judge/utils/problems.py:129 msgid "Time Limit Exceeded" msgstr "Time Limit Exceeded" @@ -2505,15 +2313,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:310 +#: judge/models/submission.py:69 judge/models/submission.py:263 msgid "execution time" msgstr "thời gian chạy" -#: judge/models/submission.py:70 judge/models/submission.py:311 +#: judge/models/submission.py:70 judge/models/submission.py:264 msgid "memory usage" msgstr "bộ nhớ sử dụng" -#: judge/models/submission.py:72 judge/models/submission.py:312 +#: judge/models/submission.py:72 judge/models/submission.py:265 msgid "points granted" msgstr "điểm" @@ -2561,56 +2369,50 @@ msgstr "được chấm lại bởi admin" msgid "was ran on pretests only" msgstr "chỉ chấm pretest" -#: judge/models/submission.py:275 templates/contest/moss.html:56 +#: judge/models/submission.py:234 templates/contest/moss.html:56 msgid "submissions" msgstr "bài nộp" -#: judge/models/submission.py:288 judge/models/submission.py:302 +#: judge/models/submission.py:241 judge/models/submission.py:255 msgid "associated submission" msgstr "bài nộp tương ứng" -#: judge/models/submission.py:306 +#: judge/models/submission.py:259 msgid "test case ID" msgstr "test case ID" -#: judge/models/submission.py:308 +#: judge/models/submission.py:261 msgid "status flag" msgstr "" -#: judge/models/submission.py:313 +#: judge/models/submission.py:266 msgid "points possible" msgstr "" -#: judge/models/submission.py:314 +#: judge/models/submission.py:267 msgid "batch number" msgstr "số thứ tự của nhóm" -#: judge/models/submission.py:316 +#: judge/models/submission.py:269 msgid "judging feedback" msgstr "phản hồi từ máy chấm" -#: judge/models/submission.py:319 +#: judge/models/submission.py:272 msgid "extended judging feedback" msgstr "phản hồi thêm từ máy chấm" -#: judge/models/submission.py:321 +#: judge/models/submission.py:274 msgid "program output" msgstr "output chương trình" -#: judge/models/submission.py:329 +#: judge/models/submission.py:282 msgid "submission test case" msgstr "cái testcase trong bài nộp" -#: judge/models/submission.py:330 +#: judge/models/submission.py:283 msgid "submission test cases" msgstr "cái testcase trong bài nộp" -#: judge/models/test_formatter.py:22 -#, fuzzy -#| msgid "test case ID" -msgid "testcase file" -msgstr "test case ID" - #: judge/models/ticket.py:10 msgid "ticket title" msgstr "tiêu đề báo cáo" @@ -2697,99 +2499,83 @@ msgstr "Trang [page]/[topage]" msgid "Page %s of %s" msgstr "Trang %s/%s" -#: judge/social_auth.py:69 judge/views/register.py:32 -msgid "A username must contain letters, numbers, or underscores" -msgstr "Tên đăng nhập phải chứa ký tự, chữ số, hoặc dấu gạch dưới" - -#: judge/social_auth.py:75 -msgid "Sorry, the username is taken." -msgstr "Xin lỗi, tên đăng nhập đã bị trùng." - -#: judge/social_auth.py:93 -msgid "Choose a username" -msgstr "Chọn tên đăng nhập" - -#: judge/social_auth.py:122 -msgid "Create your profile" -msgstr "Khởi tạo thông tin" - #: judge/tasks/contest.py:20 msgid "Recalculating contest scores" msgstr "Tính lại điểm kỳ thi" -#: judge/tasks/contest.py:59 +#: judge/tasks/contest.py:42 msgid "Running MOSS" msgstr "Đang chạy MOSS" -#: judge/tasks/submission.py:47 +#: judge/tasks/submission.py:45 msgid "Modifying submissions" msgstr "Chỉnh sửa bài nộp" -#: judge/tasks/submission.py:67 +#: judge/tasks/submission.py:65 msgid "Recalculating user points" msgstr "Tính lại điểm người dùng" -#: judge/utils/problem_data.py:81 +#: judge/utils/problem_data.py:72 msgid "Empty batches not allowed." msgstr "Nhóm test trống là không hợp lệ." -#: judge/utils/problem_data.py:89 judge/utils/problem_data.py:97 -#: judge/utils/problem_data.py:112 +#: judge/utils/problem_data.py:80 judge/utils/problem_data.py:88 +#: judge/utils/problem_data.py:103 msgid "How did you corrupt the custom checker path?" msgstr "How did you corrupt the custom checker path?" -#: judge/utils/problem_data.py:140 +#: judge/utils/problem_data.py:131 #, 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:147 +#: judge/utils/problem_data.py:138 #, 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:152 +#: judge/utils/problem_data.py:143 #, 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:179 +#: judge/utils/problem_data.py:170 #, 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:202 +#: judge/utils/problem_data.py:193 #, 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:221 +#: judge/utils/problem_data.py:212 msgid "How did you corrupt the zip path?" msgstr "" -#: judge/utils/problem_data.py:227 +#: judge/utils/problem_data.py:218 msgid "How did you corrupt the generator path?" msgstr "" -#: judge/utils/problem_data.py:245 -msgid "Invalid interactor judge" -msgstr "" - -#: judge/utils/problem_data.py:269 +#: judge/utils/problem_data.py:237 #, fuzzy -#| msgid "Invalid Return" -msgid "Invalid signature handler" -msgstr "Invalid Return" +#| msgid "How did you corrupt the custom checker path?" +msgid "How did you corrupt the interactor path?" +msgstr "How did you corrupt the custom checker path?" -#: judge/utils/problem_data.py:272 -msgid "Invalid signature header" -msgstr "" +#: judge/utils/problems.py:121 +msgid "Wrong" +msgstr "Sai" -#: judge/utils/problems.py:134 +#: judge/utils/problems.py:127 +msgid "Timeout" +msgstr "Quá thời gian" + +#: judge/utils/problems.py:130 msgid "Error" msgstr "Lỗi" -#: judge/utils/problems.py:151 +#: judge/utils/problems.py:147 msgid "Can't pass both queryset and keyword filters" msgstr "" @@ -2829,351 +2615,187 @@ msgctxt "hours and minutes" msgid "%h:%m" msgstr "%h:%m" -#: judge/utils/users.py:61 -msgid "M j, Y" -msgstr "j M, Y" - -#: judge/views/about.py:10 templates/course/course.html:27 -#: templates/organization/home.html:44 -#: templates/organization/org-right-sidebar.html:74 -#: templates/user/user-about.html:70 templates/user/user-tabs.html:4 +#: judge/views/about.py:10 templates/organization/home.html:55 +#: templates/organization/org-right-sidebar.html:70 +#: templates/user/user-about.html:83 templates/user/user-tabs.html:4 #: templates/user/users-table.html:22 msgid "About" msgstr "Giới thiệu" -#: judge/views/about.py:251 +#: judge/views/about.py:20 msgid "Custom Checker Sample" msgstr "Hướng dẫn viết trình chấm" -#: judge/views/blog.py:131 +#: judge/views/blog.py:127 #, python-format msgid "Page %d of Posts" msgstr "Trang %d" -#: judge/views/blog.py:171 +#: judge/views/blog.py:186 msgid "Ticket feed" msgstr "Báo cáo" -#: judge/views/blog.py:188 +#: judge/views/blog.py:206 msgid "Comment feed" msgstr "Bình luận" -#: judge/views/comment.py:71 judge/views/pagevote.py:32 +#: judge/views/comment.py:40 judge/views/pagevote.py:31 msgid "Messing around, are we?" msgstr "Messing around, are we?" -#: judge/views/comment.py:87 judge/views/pagevote.py:48 +#: judge/views/comment.py:56 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:113 +#: judge/views/comment.py:87 msgid "You already voted." msgstr "Bạn đã vote." -#: judge/views/comment.py:272 judge/views/organization.py:882 -#: judge/views/organization.py:1035 judge/views/organization.py:1208 +#: judge/views/comment.py:158 judge/views/organization.py:850 +#: judge/views/organization.py:996 judge/views/organization.py:1161 msgid "Edited from site" msgstr "Chỉnh sửa từ web" -#: judge/views/comment.py:293 +#: judge/views/comment.py:179 msgid "Editing comment" msgstr "Chỉnh sửa bình luận" -#: judge/views/comment.py:345 -msgid "Comment body" -msgstr "Nội dung bình luận" - -#: judge/views/comment.py:351 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/views/comment.py:360 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/views/comment.py:403 -msgid "Posted comment" -msgstr "Bình luận đã đăng" - -#: judge/views/contests.py:124 judge/views/contests.py:471 -#: judge/views/contests.py:476 judge/views/contests.py:784 +#: judge/views/contests.py:119 judge/views/contests.py:375 +#: judge/views/contests.py:380 judge/views/contests.py:637 msgid "No such contest" msgstr "Không có contest nào như vậy" -#: judge/views/contests.py:125 judge/views/contests.py:472 +#: judge/views/contests.py:120 judge/views/contests.py:376 #, 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:153 judge/views/contests.py:1561 -#: judge/views/stats.py:178 templates/contest/list.html:171 -#: templates/contest/list.html:213 templates/contest/list.html:250 -#: templates/contest/list.html:284 templates/course/course.html:59 -#: templates/course/left_sidebar.html:5 +#: judge/views/contests.py:139 judge/views/stats.py:178 #: templates/organization/org-left-sidebar.html:5 templates/stats/site.html:21 -#: templates/user/user-bookmarks.html:19 templates/user/user-bookmarks.html:80 +#: templates/user/user-bookmarks.html:54 msgid "Contests" msgstr "Kỳ thi" -#: judge/views/contests.py:331 -msgid "Start time (asc.)" -msgstr "Thời gian bắt đầu (tăng)" - -#: judge/views/contests.py:332 -msgid "Start time (desc.)" -msgstr "Thời gian bắt đầu (giảm)" - -#: judge/views/contests.py:333 judge/views/organization.py:314 -msgid "Name (asc.)" -msgstr "Tên (tăng)" - -#: judge/views/contests.py:334 judge/views/organization.py:315 -msgid "Name (desc.)" -msgstr "Tên (giảm)" - -#: judge/views/contests.py:335 -msgid "User count (asc.)" -msgstr "Số lượng tham gia (tăng)" - -#: judge/views/contests.py:336 -msgid "User count (desc.)" -msgstr "Số lượng tham gia (giảm)" - -#: judge/views/contests.py:476 +#: judge/views/contests.py:380 msgid "Could not find such contest." msgstr "Không tìm thấy kỳ thi nào như vậy." -#: judge/views/contests.py:484 +#: judge/views/contests.py:388 #, 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:570 +#: judge/views/contests.py:442 msgid "Clone Contest" msgstr "Nhân bản kỳ thi" -#: judge/views/contests.py:662 +#: judge/views/contests.py:511 msgid "Contest not ongoing" msgstr "Kỳ thi đang không diễn ra" -#: judge/views/contests.py:663 +#: judge/views/contests.py:512 #, python-format msgid "\"%s\" is not currently ongoing." msgstr "\"%s\" kỳ thi đang không diễn ra." -#: judge/views/contests.py:676 +#: judge/views/contests.py:519 +msgid "Already in contest" +msgstr "Đã ở trong kỳ thi" + +#: judge/views/contests.py:520 +#, python-format +msgid "You are already in a contest: \"%s\"." +msgstr "Bạn đã ở trong kỳ thi: \"%s\"." + +#: judge/views/contests.py:530 msgid "Banned from joining" msgstr "Bị cấm tham gia" -#: judge/views/contests.py:678 +#: judge/views/contests.py:532 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:768 +#: judge/views/contests.py:621 #, python-format msgid "Enter access code for \"%s\"" msgstr "Nhập mật khẩu truy cập cho \"%s\"" -#: judge/views/contests.py:785 +#: judge/views/contests.py:638 #, python-format msgid "You are not in contest \"%s\"." msgstr "Bạn không ở trong kỳ thi \"%s\"." -#: judge/views/contests.py:808 +#: judge/views/contests.py:661 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:866 +#: judge/views/contests.py:719 #, python-format msgid "Contests in %(month)s" msgstr "Các kỳ thi trong %(month)s" -#: judge/views/contests.py:867 +#: judge/views/contests.py:720 msgid "F Y" msgstr "F Y" -#: judge/views/contests.py:927 +#: judge/views/contests.py:780 #, python-format msgid "%s Statistics" msgstr "%s Thống kê" -#: judge/views/contests.py:1206 +#: judge/views/contests.py:1072 #, python-format msgid "%s Rankings" msgstr "%s Bảng điểm" -#: judge/views/contests.py:1217 +#: judge/views/contests.py:1083 msgid "???" msgstr "???" -#: judge/views/contests.py:1281 +#: judge/views/contests.py:1111 #, python-format msgid "Your participation in %s" msgstr "Lần tham gia trong %s" -#: judge/views/contests.py:1282 +#: judge/views/contests.py:1112 #, python-format msgid "%s's participation in %s" msgstr "Lần tham gia của %s trong %s" -#: judge/views/contests.py:1296 +#: judge/views/contests.py:1126 msgid "Live" msgstr "Trực tiếp" -#: judge/views/contests.py:1314 templates/contest/contest-tabs.html:21 +#: judge/views/contests.py:1145 templates/contest/contest-tabs.html:19 msgid "Participation" msgstr "Lần tham gia" -#: judge/views/contests.py:1363 +#: judge/views/contests.py:1194 #, python-format msgid "%s MOSS Results" msgstr "%s Kết quả MOSS" -#: judge/views/contests.py:1399 +#: judge/views/contests.py:1230 #, python-format msgid "Running MOSS for %s..." msgstr "Đang chạy MOSS cho %s..." -#: judge/views/contests.py:1422 +#: judge/views/contests.py:1253 #, python-format msgid "Contest tag: %s" msgstr "Nhãn kỳ thi: %s" -#: judge/views/contests.py:1437 judge/views/ticket.py:67 +#: judge/views/contests.py:1268 judge/views/ticket.py:72 msgid "Issue description" msgstr "Mô tả vấn đề" -#: judge/views/contests.py:1480 +#: judge/views/contests.py:1311 #, python-format msgid "New clarification for %s" msgstr "Thông báo mới cho %s" -#: judge/views/course.py:350 -#, python-format -msgid "Edit lessons for %(course_name)s" -msgstr "Chỉnh sửa bài học cho %(course_name)s" - -#: judge/views/course.py:354 -#, python-format -msgid "Edit lessons for %(course_name)s" -msgstr "Chỉnh sửa bài học cho %(course_name)s" - -#: judge/views/course.py:414 -#, fuzzy, python-format -#| msgid "Edit lessons for %(course_name)s" -msgid "Grades in %(course_name)s" -msgstr "Chỉnh sửa bài học cho %(course_name)s" - -#: judge/views/course.py:418 -#, python-format -msgid "Grades in %(course_name)s" -msgstr "Điểm trong %(course_name)s" - -#: judge/views/course.py:472 -#, fuzzy, python-format -msgid "Grades of %(lesson_name)s in %(course_name)s" -msgstr "Chỉnh sửa bài học cho %(course_name)s" - -#: judge/views/course.py:478 -#, fuzzy, python-format -msgid "" -"Grades of %(lesson_name)s in %(course_name)s" -msgstr "Điểm trong %(course_name)s" - -#: judge/views/course.py:493 judge/views/course.py:578 -#: templates/course/contest_list.html:20 -#, fuzzy -#| msgid "order" -msgid "Order" -msgstr "thứ tự" - -#: judge/views/course.py:540 judge/views/organization.py:930 -#: templates/organization/org-right-sidebar.html:47 -msgid "Add contest" -msgstr "Thêm kỳ thi" - -#: judge/views/course.py:549 -#, fuzzy -#| msgid "Added from site" -msgid "Added from course" -msgstr "Thêm từ web" - -#: judge/views/course.py:569 -#, fuzzy -#| msgid "Contests" -msgid "Contest list" -msgstr "Kỳ thi" - -#: judge/views/course.py:691 -#, fuzzy -#| msgid "Out contest" -msgid "Edit contest" -msgstr "Ngoài kỳ thi" - -#: judge/views/course.py:695 -#, fuzzy -#| msgid "Edited from site" -msgid "Edited from course" -msgstr "Chỉnh sửa từ web" - -#: judge/views/course.py:717 -#, fuzzy, python-format -#| msgid "Grades in %(course_name)s" -msgid "Edit %(contest_name)s" -msgstr "Điểm trong %(course_name)s" - -#: judge/views/custom_file_upload.py:42 -msgid "File Upload" -msgstr "Tải file lên" - -#: judge/views/email.py:21 -msgid "New Email" -msgstr "Email mới" - -#: judge/views/email.py:31 -msgid "An account with this email already exists." -msgstr "Email đã được dùng cho tài khoản khác." - -#: judge/views/email.py:55 -msgid "Email Change Request" -msgstr "Thay đổi Email" - -#: judge/views/email.py:58 -msgid "" -"We have received a request to change your email to this email. Click the " -"button below to change your email:" -msgstr "" -"Chúng tôi đã nhận được yêu cầu thay đổi địa chỉ email của bạn thành địa chỉ " -"email này. Vui lòng nhấp vào nút bên dưới để thay đổi địa chỉ email của bạn:" - -#: judge/views/email.py:60 -msgid "Email Change" -msgstr "Thay đổi Email" - -#: judge/views/email.py:61 -msgid "Change Email" -msgstr "Thay đổi Email" - -#: judge/views/email.py:83 templates/user/edit-profile.html:127 -msgid "Change email" -msgstr "Thay đổi email" - -#: judge/views/email.py:106 -msgid "Success" -msgstr "Thành công" - -#: judge/views/email.py:110 -msgid "Invalid" -msgstr "Không hợp lệ" - -#: judge/views/email.py:119 -msgid "Email change pending" -msgstr "Yêu cầu thay đổi email đang đợi xác thực." - #: judge/views/error.py:17 msgid "404 error" msgstr "Lỗi 404" @@ -3193,135 +2815,107 @@ msgstr "không có quyền cho %s" msgid "corrupt page %s" msgstr "trang bị sập %s" -#: judge/views/internal.py:24 +#: judge/views/internal.py:12 #, fuzzy #| msgid "contest problems" msgid "Internal problems" msgstr "bài trong kỳ thi" -#: judge/views/internal.py:106 -#, fuzzy -#| msgid "request time" -msgid "Request times" -msgstr "thời gian đăng ký" - #: judge/views/language.py:12 templates/status/judge-status-table.html:9 #: templates/status/status-tabs.html:5 msgid "Runtimes" msgstr "Runtimes" -#: judge/views/markdown_editor.py:12 -msgid "Markdown Editor" -msgstr "" - -#: judge/views/notification.py:32 +#: judge/views/notification.py:43 #, python-format msgid "Notifications (%d unseen)" msgstr "Thông báo (%d chưa xem)" -#: judge/views/organization.py:157 judge/views/organization.py:164 +#: judge/views/organization.py:148 judge/views/organization.py:154 msgid "No such organization" msgstr "Không có tổ chức như vậy" -#: judge/views/organization.py:158 +#: judge/views/organization.py:149 #, 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:165 +#: judge/views/organization.py:155 msgid "Could not find such organization." msgstr "" -#: judge/views/organization.py:189 +#: judge/views/organization.py:178 msgid "Can't edit organization" msgstr "Không thể chỉnh sửa tổ chức" -#: judge/views/organization.py:190 +#: judge/views/organization.py:179 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:202 judge/views/organization.py:400 +#: judge/views/organization.py:191 judge/views/organization.py:359 +#, fuzzy +#| msgid "Can't edit organization" msgid "Can't access organization" -msgstr "Không thể truy cập nhóm" +msgstr "Không thể chỉnh sửa tổ chức" -#: judge/views/organization.py:203 judge/views/organization.py:401 +#: judge/views/organization.py:192 judge/views/organization.py:360 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:248 judge/views/stats.py:184 -#: templates/contest/list.html:78 templates/problem/list-base.html:90 -#: templates/stats/site.html:33 templates/user/user-list-tabs.html:6 +#: judge/views/organization.py:250 judge/views/register.py:48 +#: judge/views/stats.py:184 templates/contest/list.html:85 +#: templates/problem/list-base.html:98 templates/stats/site.html:33 +#: templates/user/user-left-sidebar.html:4 templates/user/user-list-tabs.html:6 msgid "Groups" msgstr "Nhóm" -#: judge/views/organization.py:316 -msgid "Member count (asc.)" -msgstr "Số lượng thành viên (tăng)" - -#: judge/views/organization.py:317 -msgid "Member count (desc.)" -msgstr "Số lượng thành viên (giảm)" - -#: judge/views/organization.py:407 +#: judge/views/organization.py:366 #, python-format msgid "%s Members" msgstr "%s Thành viên" -#: judge/views/organization.py:529 +#: judge/views/organization.py:506 #, python-brace-format msgid "All submissions in {0}" msgstr "Bài nộp trong {0}" -#: judge/views/organization.py:537 judge/views/submission.py:858 -msgid "Submissions in" -msgstr "Bài nộp trong" - -#: judge/views/organization.py:562 judge/views/organization.py:568 -#: judge/views/organization.py:575 +#: judge/views/organization.py:536 judge/views/organization.py:542 +#: judge/views/organization.py:549 msgid "Joining group" msgstr "Tham gia nhóm" -#: judge/views/organization.py:563 +#: judge/views/organization.py:537 msgid "You are already in the group." msgstr "Bạn đã ở trong nhóm." -#: judge/views/organization.py:568 +#: judge/views/organization.py:542 msgid "This group is not open." msgstr "Nhóm này là nhóm kín." -#: judge/views/organization.py:576 -#, 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:591 +#: judge/views/organization.py:565 msgid "Leaving group" msgstr "Rời nhóm" -#: judge/views/organization.py:592 +#: judge/views/organization.py:566 #, python-format msgid "You are not in \"%s\"." msgstr "Bạn không ở trong \"%s\"." -#: judge/views/organization.py:617 +#: judge/views/organization.py:591 #, python-format msgid "Request to join %s" msgstr "Đăng ký tham gia %s" -#: judge/views/organization.py:647 +#: judge/views/organization.py:622 msgid "Join request detail" msgstr "Chi tiết đơn đăng ký" -#: judge/views/organization.py:681 -msgid "Manage join requests" -msgstr "Quản lý đơn đăng ký" - -#: judge/views/organization.py:685 +#: judge/views/organization.py:670 #, python-format msgid "Managing join requests for %s" msgstr "Quản lý đơn đăng ký cho %s" -#: judge/views/organization.py:725 +#: judge/views/organization.py:710 #, python-format msgid "" "Your organization can only receive %d more members. You cannot approve %d " @@ -3330,221 +2924,190 @@ 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:743 +#: judge/views/organization.py:728 #, python-format msgid "Approved %d user." msgid_plural "Approved %d users." msgstr[0] "Đã chấp thuận %d người." -#: judge/views/organization.py:746 +#: judge/views/organization.py:731 #, python-format msgid "Rejected %d user." msgid_plural "Rejected %d users." msgstr[0] "Đã từ chối %d người." -#: judge/views/organization.py:786 +#: judge/views/organization.py:771 #, python-format msgid "Add member for %s" msgstr "Thêm thành viên cho %s" -#: judge/views/organization.py:802 +#: judge/views/organization.py:783 #, fuzzy #| msgid "Edited from site" msgid "Added members from site" msgstr "Chỉnh sửa từ web" -#: judge/views/organization.py:822 judge/views/organization.py:830 -#: judge/views/organization.py:839 +#: judge/views/organization.py:803 judge/views/organization.py:811 msgid "Can't kick user" msgstr "Không thể đuổi" -#: judge/views/organization.py:823 +#: judge/views/organization.py:804 msgid "The user you are trying to kick does not exist!" msgstr "" -#: judge/views/organization.py:831 +#: judge/views/organization.py:812 #, python-format msgid "The user you are trying to kick is not in organization: %s." msgstr "" -#: judge/views/organization.py:840 -#, fuzzy -#| msgid "Are you sure you want to leave this organization?" -msgid "The user you are trying to kick is an organization admin." -msgstr "Bạn có chắc muốn rời tổ chức?" - -#: judge/views/organization.py:845 -#, fuzzy -#| msgid "Add members" -msgid "Kicked member" -msgstr "Thêm thành viên" - -#: judge/views/organization.py:865 judge/views/organization.py:1024 -#, python-format +#: judge/views/organization.py:833 judge/views/organization.py:985 +#, fuzzy, python-format +#| msgid "Editing %s" msgid "Edit %s" -msgstr "Chỉnh sửa %s" +msgstr "Đang chỉnh sửa %s" -#: judge/views/organization.py:893 templates/organization/search-form.html:19 +#: judge/views/organization.py:861 templates/organization/list.html:45 msgid "Create group" msgstr "Tạo nhóm" -#: judge/views/organization.py:908 +#: judge/views/organization.py:876 msgid "Exceeded limit" msgstr "" -#: judge/views/organization.py:909 +#: judge/views/organization.py:877 #, python-format msgid "You created too many groups. You can only create at most %d groups" msgstr "" -#: judge/views/organization.py:914 judge/views/organization.py:939 -#: judge/views/organization.py:1093 +#: judge/views/organization.py:882 judge/views/organization.py:907 +#: judge/views/organization.py:1051 msgid "Added from site" msgstr "Thêm từ web" -#: judge/views/organization.py:973 judge/views/organization.py:1140 +#: judge/views/organization.py:898 +#: templates/organization/org-right-sidebar.html:55 +msgid "Add contest" +msgstr "Thêm kỳ thi" + +#: judge/views/organization.py:941 judge/views/organization.py:1103 msgid "Permission denied" msgstr "Truy cập bị từ chối" -#: judge/views/organization.py:974 +#: judge/views/organization.py:942 #, 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:1028 templates/blog/blog.html:31 -#: templates/comments/content-list.html:53 -#: templates/comments/content-list.html:66 -#: templates/contest/contest-tabs.html:36 templates/contest/macros.html:14 -#: templates/contest/tag-title.html:9 templates/course/contest_list.html:25 -#: templates/flatpages/admin_link.html:3 templates/license.html:10 -#: templates/organization/blog/pending.html:56 -#: templates/problem/editorial.html:15 templates/problem/feed/items.html:50 -#: templates/test_formatter/download_test_formatter.html:83 -msgid "Edit" -msgstr "Chỉnh sửa" - -#: judge/views/organization.py:1082 +#: judge/views/organization.py:1040 #, python-format msgid "Add blog for %s" msgstr "Thêm bài đăng cho %s" -#: judge/views/organization.py:1134 -#, fuzzy -#| msgid "Those who can edit this organization" -msgid "This blog does not belong to this organization" -msgstr "Những người có thể chỉnh sửa tổ chức" - -#: judge/views/organization.py:1136 +#: judge/views/organization.py:1104 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:1193 +#: judge/views/organization.py:1136 #, python-format msgid "Edit blog %s" msgstr "Chỉnh sửa %s" -#: judge/views/organization.py:1239 +#: judge/views/organization.py:1187 #, python-format msgid "Pending blogs in %s" msgstr "Bài đang đợi duyệt trong %s" -#: judge/views/problem.py:135 +#: judge/views/problem.py:132 msgid "No such problem" msgstr "Không có bài nào như vậy" -#: judge/views/problem.py:136 +#: judge/views/problem.py:133 #, python-format msgid "Could not find a problem with the code \"%s\"." msgstr "Không tìm thấy bài tập với mã bài \"%s\"." -#: judge/views/problem.py:203 +#: judge/views/problem.py:200 #, python-brace-format msgid "Editorial for {0}" msgstr "Hướng dẫn cho {0}" -#: judge/views/problem.py:207 +#: judge/views/problem.py:204 #, python-brace-format msgid "Editorial for {0}" msgstr "Hướng dẫn cho {0}" -#: judge/views/problem.py:460 templates/contest/contest.html:117 -#: templates/course/lesson.html:14 +#: judge/views/problem.py:463 templates/contest/contest.html:97 #: templates/organization/org-left-sidebar.html:4 -#: templates/profile-table.html:25 templates/user/user-about.html:28 -#: templates/user/user-bookmarks.html:16 templates/user/user-tabs.html:5 -#: templates/user/users-table.html:19 +#: templates/user/user-about.html:28 templates/user/user-bookmarks.html:34 +#: templates/user/user-tabs.html:5 templates/user/users-table.html:19 msgid "Problems" msgstr "Bài tập" -#: judge/views/problem.py:842 +#: judge/views/problem.py:837 msgid "Problem feed" msgstr "Bài tập" -#: judge/views/problem.py:1046 judge/views/problem.py:1079 -msgid "