diff --git a/502.html b/502.html
index 98f4dbb..a65c3cd 100644
--- a/502.html
+++ b/502.html
@@ -49,7 +49,7 @@
" + escape(code) + "") +def highlight_code(code, language, linenos=True, title=None): + linenos_option = 'linenums="1"' if linenos else "" + title_option = f'title="{title}"' if title else "" + options = f"{{.{language} {linenos_option} {title_option}}}" - -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), - ) - ) + value = f"```{options}\n{code}\n```\n" + return mark_safe(markdown(value)) diff --git a/judge/jinja2/__init__.py b/judge/jinja2/__init__.py index 93ab0ad..e24ea8c 100644 --- a/judge/jinja2/__init__.py +++ b/judge/jinja2/__init__.py @@ -22,6 +22,7 @@ from . import ( social, spaceless, timedelta, + comment, ) from . import registry diff --git a/judge/jinja2/comment.py b/judge/jinja2/comment.py new file mode 100644 index 0000000..6baa365 --- /dev/null +++ b/judge/jinja2/comment.py @@ -0,0 +1,12 @@ +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 eb0ec41..ce7cf32 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=_("on {time}")): +def relative_time(time, format=_("N j, Y, g:i a"), rel=_("{time}"), abs=_("{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 b6e8a83..175992f 100644 --- a/judge/jinja2/gravatar.py +++ b/judge/jinja2/gravatar.py @@ -10,10 +10,11 @@ from . import registry @registry.function def gravatar(profile, size=80, default=None, profile_image=None, email=None): - if profile_image: - return profile_image - if profile and profile.profile_image_url: - return profile.profile_image_url + 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 if default is None: diff --git a/judge/jinja2/markdown/__init__.py b/judge/jinja2/markdown/__init__.py index fa78791..18355d4 100644 --- a/judge/jinja2/markdown/__init__.py +++ b/judge/jinja2/markdown/__init__.py @@ -1,112 +1,7 @@ from .. import registry -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 = list(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"] +from judge.markdown import markdown as _markdown @registry.filter def markdown(value, lazy_load=False): - 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: - 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 new file mode 100644 index 0000000..f981745 --- /dev/null +++ b/judge/views/test_formatter/tf_logic.py @@ -0,0 +1,116 @@ +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 new file mode 100644 index 0000000..071976d --- /dev/null +++ b/judge/views/test_formatter/tf_pattern.py @@ -0,0 +1,268 @@ +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 new file mode 100644 index 0000000..919b069 --- /dev/null +++ b/judge/views/test_formatter/tf_utils.py @@ -0,0 +1,15 @@ +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/user.py b/judge/views/user.py index db073f1..6feaffa 100644 --- a/judge/views/user.py +++ b/judge/views/user.py @@ -35,23 +35,42 @@ 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 -from judge.models import Profile, Rating, Submission, Friend +from judge.forms import UserForm, ProfileForm, ProfileInfoForm +from judge.models import ( + Profile, + Rating, + Submission, + Friend, + ProfileInfo, + BlogPost, + Problem, + Contest, + Solution, +) 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 ( 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", @@ -100,12 +119,12 @@ class UserPage(TitleMixin, UserMixin, DetailView): def get_title(self): return ( _("My account") - if self.request.user == self.object.user - else _("User %s") % self.object.user.username + if self.request.profile == self.object + else _("User %s") % self.object.username ) def get_content_title(self): - username = self.object.user.username + username = self.object.username css_class = self.object.css_class return mark_safe(f'{username}') @@ -142,22 +161,10 @@ class UserPage(TitleMixin, UserMixin, DetailView): rating = self.object.ratings.order_by("-contest__end_time")[:1] context["rating"] = rating[0] if rating else None - context["rank"] = ( - Profile.objects.filter( - is_unlisted=False, - performance_points__gt=self.object.performance_points, - ).count() - + 1 - ) + context["points_rank"] = get_points_rank(self.object) if rating: - context["rating_rank"] = ( - Profile.objects.filter( - is_unlisted=False, - rating__gt=self.object.rating, - ).count() - + 1 - ) + context["rating_rank"] = get_rating_rank(self.object) context["rated_users"] = Profile.objects.filter( is_unlisted=False, rating__isnull=False ).count() @@ -185,42 +192,9 @@ 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"] = ( - self.object.ratings.order_by("-contest__end_time") - .select_related("contest") - .defer("contest__description") - ) + ratings = context["ratings"] = get_contest_ratings(self.object) context["rating_data"] = mark_safe( json.dumps( @@ -229,7 +203,9 @@ class UserAboutPage(UserPage): "label": rating.contest.name, "rating": rating.rating, "ranking": rating.rank, - "link": reverse("contest_ranking", args=(rating.contest.key,)), + "link": reverse("contest_ranking", args=(rating.contest.key,)) + + "#!" + + self.object.username, "timestamp": (rating.contest.end_time - EPOCH).total_seconds() * 1000, "date": date_format( @@ -244,7 +220,7 @@ class UserAboutPage(UserPage): ) ) - context["awards"] = self.get_awards(ratings) + context["awards"] = get_awards(self.object) if ratings: user_data = self.object.ratings.aggregate(Min("rating"), Max("rating")) @@ -288,19 +264,6 @@ 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" @@ -357,17 +320,49 @@ class UserProblemsPage(UserPage): return context -class UserBookMarkPage(UserPage): +class UserBookMarkPage(DiggPaginatorMixin, ListView, 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) - 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") + 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 return context @@ -403,21 +398,25 @@ class UserPerformancePointsAjax(UserProblemsPage): @login_required def edit_profile(request): profile = request.profile + profile_info, created = ProfileInfo.objects.get_or_create(profile=profile) 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) if form_user.is_valid() and form.is_valid(): - with transaction.atomic(), revisions.create_revision(): + with 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 @@ -428,9 +427,9 @@ 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", }, @@ -457,14 +456,13 @@ class UserList(QueryStringSortMixin, InfinitePaginationMixin, TitleMixin, ListVi 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: @@ -472,11 +470,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) ) @@ -592,3 +590,17 @@ 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 64c58f9..acd3469 100644 --- a/judge/views/volunteer.py +++ b/judge/views/volunteer.py @@ -19,15 +19,14 @@ def vote_problem(request): except Exception as e: return HttpResponseBadRequest() - 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() + 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/datetime.py b/judge/widgets/datetime.py index 15bb383..d205e1c 100644 --- a/judge/widgets/datetime.py +++ b/judge/widgets/datetime.py @@ -1,24 +1,49 @@ 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): - template_name = "widgets/datetimepicker.html" + input_type = "datetime-local" - 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 + 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") - @property - def media(self): - css_url = "/static/datetime-picker/datetimepicker.min.css" - js_url = "/static/datetime-picker/datetimepicker.full.min.js" - return forms.Media( - js=[js_url], - css={"screen": [css_url]}, + final_attrs = self.build_attrs( + attrs, {"type": self.input_type, "name": name, "value": value} ) + 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/pagedown.py b/judge/widgets/pagedown.py index c30940b..b0403d5 100644 --- a/judge/widgets/pagedown.py +++ b/judge/widgets/pagedown.py @@ -10,8 +10,8 @@ from judge.widgets.mixins import CompressorWidgetMixin __all__ = [ "PagedownWidget", "AdminPagedownWidget", - "MathJaxPagedownWidget", - "MathJaxAdminPagedownWidget", + "KatexPagedownWidget", + "KatexAdminPagedownWidget", "HeavyPreviewPageDownWidget", "HeavyPreviewAdminPageDownWidget", ] @@ -21,8 +21,8 @@ try: except ImportError: PagedownWidget = None AdminPagedownWidget = None - MathJaxPagedownWidget = None - MathJaxAdminPagedownWidget = None + KatexPagedownWidget = None + KatexAdminPagedownWidget = None HeavyPreviewPageDownWidget = None HeavyPreviewAdminPageDownWidget = None else: @@ -61,15 +61,19 @@ else: } js = ["admin/js/pagedown.js"] - class MathJaxPagedownWidget(PagedownWidget): + class KatexPagedownWidget(PagedownWidget): class Media: + css = { + "all": ["https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css"] + } js = [ - "mathjax3_config.js", - "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.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", "pagedown_math.js", ] - class MathJaxAdminPagedownWidget(AdminPagedownWidget, MathJaxPagedownWidget): + class KatexAdminPagedownWidget(AdminPagedownWidget, KatexPagedownWidget): pass class HeavyPreviewPageDownWidget(PagedownWidget): @@ -112,12 +116,11 @@ else: js = ["dmmd-preview.js"] class HeavyPreviewAdminPageDownWidget( - AdminPagedownWidget, HeavyPreviewPageDownWidget + KatexPagedownWidget, AdminPagedownWidget, HeavyPreviewPageDownWidget ): class Media: css = { "all": [ - "pygment-github.css", "table.css", "ranks.css", "dmmd-preview.css", diff --git a/locale/vi/LC_MESSAGES/django.po b/locale/vi/LC_MESSAGES/django.po index 338f920..a6c4b1d 100644 --- a/locale/vi/LC_MESSAGES/django.po +++ b/locale/vi/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: lqdoj2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-02 08:11+0700\n" +"POT-Creation-Date: 2024-06-19 05:23+0700\n" "PO-Revision-Date: 2021-07-20 03:44\n" "Last-Translator: Icyene\n" "Language-Team: Vietnamese\n" @@ -18,301 +18,310 @@ msgstr "" "X-Crowdin-Project-ID: 466004\n" "X-Crowdin-File-ID: 5\n" -#: chat_box/models.py:54 chat_box/models.py:80 chat_box/models.py:96 -#: judge/admin/interface.py:150 judge/models/contest.py:647 -#: judge/models/contest.py:853 judge/models/course.py:115 -#: judge/models/profile.py:433 judge/models/profile.py:511 +#: chat_box/models.py:22 chat_box/models.py:83 +msgid "last seen" +msgstr "xem lần cuối" + +#: chat_box/models.py:54 chat_box/models.py:79 chat_box/models.py:95 +#: judge/admin/interface.py:151 judge/models/contest.py:693 +#: judge/models/contest.py:899 judge/models/course.py:129 +#: judge/models/profile.py:462 judge/models/profile.py:536 msgid "user" msgstr "người dùng" -#: chat_box/models.py:56 judge/models/comment.py:44 +#: chat_box/models.py:56 judge/models/comment.py:45 #: judge/models/notification.py:17 msgid "posted time" msgstr "thời gian đăng" -#: chat_box/models.py:58 judge/models/comment.py:49 +#: chat_box/models.py:58 judge/models/comment.py:50 msgid "body of comment" msgstr "nội dung bình luận" -#: chat_box/models.py:84 -msgid "last seen" -msgstr "xem lần cuối" - #: chat_box/views.py:44 msgid "LQDOJ Chat" msgstr "" -#: chat_box/views.py:417 +#: chat_box/views.py:444 msgid "Recent" msgstr "Gần đây" -#: chat_box/views.py:421 templates/base.html:187 -#: templates/comments/content-list.html:78 -#: templates/contest/contest-list-tabs.html:4 -#: templates/contest/ranking-table.html:47 templates/internal/problem.html:62 +#: chat_box/views.py:448 templates/base.html:196 +#: templates/comments/content-list.html:72 +#: templates/contest/contest-list-tabs.html:6 +#: templates/contest/ranking-table.html:52 templates/course/left_sidebar.html:8 +#: templates/internal/problem/problem.html:63 #: templates/organization/org-left-sidebar.html:12 #: templates/problem/left-sidebar.html:6 #: templates/problem/problem-list-tabs.html:6 -#: templates/submission/info-base.html:12 templates/submission/list.html:395 +#: templates/submission/info-base.html:12 templates/submission/list.html:394 #: templates/submission/submission-list-tabs.html:15 msgid "Admin" msgstr "Admin" -#: dmoj/settings.py:364 +#: dmoj/settings.py:365 msgid "Vietnamese" msgstr "Tiếng Việt" -#: dmoj/settings.py:365 +#: dmoj/settings.py:366 msgid "English" msgstr "" -#: dmoj/urls.py:109 +#: dmoj/urls.py:107 msgid "Activation key invalid" msgstr "Mã kích hoạt không hợp lệ" -#: dmoj/urls.py:114 +#: dmoj/urls.py:112 msgid "Register" msgstr "Đăng ký" -#: dmoj/urls.py:121 +#: dmoj/urls.py:119 msgid "Registration Completed" msgstr "Đăng ký hoàn thành" -#: dmoj/urls.py:129 +#: dmoj/urls.py:127 msgid "Registration not allowed" msgstr "Đăng ký không thành công" -#: dmoj/urls.py:137 +#: dmoj/urls.py:135 msgid "Login" msgstr "Đăng nhập" -#: dmoj/urls.py:225 templates/base.html:111 +#: dmoj/urls.py:221 templates/base.html:118 +#: templates/course/left_sidebar.html:2 #: templates/organization/org-left-sidebar.html:2 msgid "Home" msgstr "Trang chủ" -#: judge/admin/comments.py:58 +#: judge/admin/comments.py:57 #, 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:65 +#: judge/admin/comments.py:64 msgid "Hide comments" msgstr "Ẩn bình luận" -#: judge/admin/comments.py:72 +#: judge/admin/comments.py:71 #, 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:79 +#: judge/admin/comments.py:78 msgid "Unhide comments" msgstr "Hiện bình luận" -#: judge/admin/contest.py:37 +#: judge/admin/contest.py:45 msgid "Included contests" msgstr "" -#: judge/admin/contest.py:79 judge/admin/volunteer.py:54 -#: templates/contest/clarification.html:42 templates/contest/contest.html:105 +#: judge/admin/contest.py:87 judge/admin/volunteer.py:54 +#: templates/contest/clarification.html:42 templates/contest/contest.html:116 #: templates/contest/moss.html:41 templates/internal/left-sidebar.html:2 -#: templates/internal/problem.html:40 templates/problem/list.html:17 +#: templates/internal/problem/problem.html:41 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:155 +#: judge/admin/contest.py:183 templates/base.html:211 msgid "Settings" msgstr "Cài đặt" -#: judge/admin/contest.py:169 +#: judge/admin/contest.py:198 msgid "Scheduling" msgstr "" -#: judge/admin/contest.py:173 +#: judge/admin/contest.py:202 msgid "Details" msgstr "Chi tiết" -#: judge/admin/contest.py:185 templates/contest/list.html:263 -#: templates/contest/list.html:304 templates/contest/list.html:349 -#: templates/contest/list.html:386 +#: judge/admin/contest.py:214 templates/contest/macros.html:83 +#: templates/contest/macros.html:93 msgid "Format" msgstr "Thể thức" -#: judge/admin/contest.py:189 templates/contest/ranking-table.html:5 -#: templates/user/user-about.html:15 templates/user/user-about.html:45 +#: 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 msgid "Rating" msgstr "" -#: judge/admin/contest.py:201 +#: judge/admin/contest.py:230 msgid "Access" msgstr "Truy cập" -#: judge/admin/contest.py:211 judge/admin/problem.py:220 +#: judge/admin/contest.py:240 judge/admin/problem.py:233 msgid "Justice" msgstr "Xử phạt" -#: judge/admin/contest.py:331 +#: judge/admin/contest.py:368 #, 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:338 +#: judge/admin/contest.py:375 msgid "Mark contests as visible" msgstr "Đánh dấu hiển thị các kỳ thi" -#: judge/admin/contest.py:349 +#: judge/admin/contest.py:386 #, 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:356 +#: judge/admin/contest.py:393 msgid "Mark contests as hidden" msgstr "Ẩn các kỳ thi" -#: judge/admin/contest.py:377 judge/admin/submission.py:241 +#: judge/admin/contest.py:414 judge/admin/submission.py:241 #, 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:485 +#: judge/admin/contest.py:522 #, 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:492 +#: judge/admin/contest.py:529 msgid "Recalculate results" msgstr "Tính toán lại kết quả" -#: judge/admin/contest.py:497 judge/admin/organization.py:99 +#: judge/admin/contest.py:534 judge/admin/organization.py:99 msgid "username" msgstr "tên đăng nhập" -#: judge/admin/contest.py:503 templates/base.html:239 +#: judge/admin/contest.py:540 templates/base.html:251 msgid "virtual" msgstr "ảo" -#: judge/admin/interface.py:35 judge/models/interface.py:50 +#: judge/admin/interface.py:35 judge/models/interface.py:51 msgid "link path" msgstr "đường dẫn" -#: judge/admin/interface.py:94 +#: judge/admin/interface.py:95 templates/course/lesson.html:10 msgid "Content" msgstr "Nội dung" -#: judge/admin/interface.py:95 +#: judge/admin/interface.py:96 msgid "Summary" msgstr "Tổng kết" -#: judge/admin/interface.py:217 +#: judge/admin/interface.py:218 msgid "object" msgstr "" -#: judge/admin/interface.py:227 +#: judge/admin/interface.py:228 msgid "Diff" msgstr "" -#: judge/admin/interface.py:232 +#: judge/admin/interface.py:233 msgid "diff" msgstr "" -#: judge/admin/organization.py:61 judge/admin/problem.py:277 -#: judge/admin/profile.py:117 +#: judge/admin/organization.py:61 judge/admin/problem.py:290 +#: judge/admin/profile.py:122 msgid "View on site" msgstr "Xem trên trang" -#: judge/admin/problem.py:55 +#: judge/admin/problem.py:56 msgid "Describe the changes you made (optional)" msgstr "Mô tả các thay đổi (tùy chọn)" -#: judge/admin/problem.py:111 +#: 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 msgid "Memory unit" msgstr "Đơn vị bộ nhớ" -#: judge/admin/problem.py:213 +#: judge/admin/problem.py:226 msgid "Social Media" msgstr "Mạng Xã Hội" -#: judge/admin/problem.py:216 +#: judge/admin/problem.py:229 msgid "Taxonomy" msgstr "" -#: judge/admin/problem.py:217 judge/admin/problem.py:452 -#: templates/contest/contest.html:106 -#: templates/contest/contests_summary.html:41 templates/problem/data.html:533 +#: judge/admin/problem.py:230 judge/admin/problem.py:463 +#: templates/contest/contest.html:117 +#: templates/contest/contests_summary.html:41 templates/problem/data.html:535 #: 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:52 templates/user/user-problems.html:58 +#: templates/user/user-about.html:50 templates/user/user-problems.html:58 msgid "Points" msgstr "Điểm" -#: judge/admin/problem.py:218 +#: judge/admin/problem.py:231 msgid "Limits" msgstr "Giới hạn" -#: judge/admin/problem.py:219 judge/admin/submission.py:351 -#: templates/base.html:155 templates/stats/tab.html:4 -#: templates/submission/list.html:347 +#: judge/admin/problem.py:232 judge/admin/submission.py:351 +#: templates/base.html:162 templates/stats/tab.html:4 +#: templates/submission/list.html:346 msgid "Language" msgstr "Ngôn ngữ" -#: judge/admin/problem.py:221 +#: judge/admin/problem.py:234 msgid "History" msgstr "Lịch sử" -#: judge/admin/problem.py:273 templates/problem/list-base.html:93 +#: judge/admin/problem.py:286 templates/problem/list-base.html:93 msgid "Authors" msgstr "Các tác giả" -#: judge/admin/problem.py:294 +#: judge/admin/problem.py:307 #, 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:301 +#: judge/admin/problem.py:314 msgid "Mark problems as public" msgstr "Công khai bài tập" -#: judge/admin/problem.py:310 +#: judge/admin/problem.py:323 #, 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:317 +#: judge/admin/problem.py:330 msgid "Mark problems as private" msgstr "Đánh dấu các bài tập là riêng tư" -#: judge/admin/problem.py:446 judge/admin/submission.py:314 +#: judge/admin/problem.py:457 judge/admin/submission.py:314 #: templates/problem/list.html:18 templates/problem/list.html:37 msgid "Problem code" msgstr "Mã bài" -#: judge/admin/problem.py:458 judge/admin/submission.py:320 +#: judge/admin/problem.py:469 judge/admin/submission.py:320 msgid "Problem name" msgstr "Tên bài" -#: judge/admin/problem.py:464 +#: judge/admin/problem.py:475 #, fuzzy #| msgid "contest rating" msgid "Voter rating" msgstr "rating kỳ thi" -#: judge/admin/problem.py:470 +#: judge/admin/problem.py:481 #, fuzzy #| msgid "Total points" msgid "Voter point" msgstr "Tổng điểm" -#: judge/admin/problem.py:476 +#: judge/admin/problem.py:487 msgid "Vote" msgstr "" @@ -320,36 +329,36 @@ msgstr "" msgid "timezone" msgstr "múi giờ" -#: judge/admin/profile.py:126 judge/admin/submission.py:327 +#: judge/admin/profile.py:131 judge/admin/submission.py:327 #: templates/notification/list.html:9 #: templates/organization/requests/log.html:9 #: templates/organization/requests/pending.html:19 -#: templates/ticket/list.html:263 +#: templates/ticket/list.html:265 msgid "User" msgstr "Thành viên" -#: judge/admin/profile.py:132 templates/registration/registration_form.html:40 -#: templates/user/edit-profile.html:116 templates/user/import/table_csv.html:8 +#: judge/admin/profile.py:137 templates/registration/registration_form.html:40 +#: templates/user/edit-profile.html:123 templates/user/import/table_csv.html:8 msgid "Email" msgstr "Email" -#: judge/admin/profile.py:138 judge/views/register.py:36 +#: judge/admin/profile.py:143 judge/views/register.py:36 #: templates/registration/registration_form.html:68 -#: templates/user/edit-profile.html:140 +#: templates/user/edit-profile.html:147 msgid "Timezone" msgstr "Múi giờ" -#: judge/admin/profile.py:144 +#: judge/admin/profile.py:149 msgid "date joined" msgstr "ngày tham gia" -#: judge/admin/profile.py:154 +#: judge/admin/profile.py:159 #, 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:161 +#: judge/admin/profile.py:166 msgid "Recalculate scores" msgstr "Tính điểm lại" @@ -408,7 +417,7 @@ msgstr "Bạn không có quyền chấm lại nhiều bài nộp như vậy." 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:226 +#: judge/admin/submission.py:302 judge/views/problem_manage.py:218 #, python-format msgid "%d submission were successfully rescored." msgid_plural "%d submissions were successfully rescored." @@ -418,14 +427,15 @@ msgstr[0] "%d bài nộp đã được tính điểm lại." msgid "Rescore the selected submissions" msgstr "Tính điểm lại cái bài nộp" -#: judge/admin/submission.py:332 templates/contest/list.html:248 -#: templates/contest/list.html:293 templates/contest/list.html:338 -#: templates/contest/list.html:380 templates/notification/list.html:12 +#: judge/admin/submission.py:332 templates/contest/list.html:174 +#: templates/contest/list.html:216 templates/contest/list.html:253 +#: templates/contest/list.html:287 templates/notification/list.html:12 #: 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 msgid "Time" msgstr "Thời gian" @@ -455,7 +465,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.html:81 +#: judge/admin/volunteer.py:60 templates/internal/problem/votes.html:17 #: templates/problem/list.html:20 templates/problem/list.html:44 msgid "Types" msgstr "Dạng" @@ -464,23 +474,6 @@ msgstr "Dạng" msgid "Online Judge" msgstr "" -#: judge/comments.py:63 -msgid "Comment body" -msgstr "Nội dung bình luận" - -#: judge/comments.py:69 judge/views/ticket.py:73 -msgid "Your part is silent, little toad." -msgstr "Bạn không được phép bình luận." - -#: judge/comments.py:78 templates/comments/list.html:17 -msgid "" -"You need to have solved at least one problem before your voice can be heard." -msgstr "Bạn phải giải ít nhất một bài trước khi được phép bình luận." - -#: judge/comments.py:122 -msgid "Posted comment" -msgstr "Bình luận đã đăng" - #: judge/contest_format/atcoder.py:19 msgid "AtCoder" msgstr "" @@ -509,79 +502,109 @@ msgstr "IOI mới" msgid "Ultimate" msgstr "" -#: judge/forms.py:113 +#: 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:76 +#: templates/course/list.html:8 +msgid "Courses" +msgstr "Khóa học" + +#: judge/forms.py:120 msgid "File size exceeds the maximum allowed limit of 5MB." msgstr "File tải lên không được quá 5MB." -#: judge/forms.py:144 +#: judge/forms.py:151 msgid "Any judge" msgstr "" -#: judge/forms.py:344 +#: judge/forms.py:351 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:345 judge/views/stats.py:166 templates/stats/site.html:27 +#: judge/forms.py:352 judge/views/stats.py:166 templates/stats/site.html:27 msgid "New users" msgstr "Thành viên mới" -#: judge/forms.py:362 +#: judge/forms.py:369 #, 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:421 judge/views/register.py:30 -#: templates/registration/registration_form.html:34 -#: templates/user/base-users-table.html:5 -#: templates/user/import/table_csv.html:4 -msgid "Username" -msgstr "Tên đăng nhập" +#: judge/forms.py:429 +msgid "Username/Email" +msgstr "Tên đăng nhập / Email" -#: judge/forms.py:422 judge/views/email.py:22 +#: judge/forms.py:431 judge/views/email.py:22 #: templates/registration/registration_form.html:46 #: templates/registration/registration_form.html:60 -#: templates/user/edit-profile.html:108 templates/user/import/table_csv.html:5 +#: templates/user/edit-profile.html:115 templates/user/import/table_csv.html:5 msgid "Password" msgstr "Mật khẩu" -#: judge/forms.py:448 +#: judge/forms.py:457 msgid "Two Factor Authentication tokens must be 6 decimal digits." msgstr "Two Factor Authentication phải chứa 6 chữ số." -#: judge/forms.py:461 templates/registration/totp_auth.html:32 +#: judge/forms.py:470 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:468 judge/models/problem.py:130 +#: judge/forms.py:477 judge/models/problem.py:133 msgid "Problem code must be ^[a-z0-9]+$" msgstr "Mã bài phải có dạng ^[a-z0-9]+$" -#: judge/forms.py:475 +#: judge/forms.py:484 msgid "Problem with code already exists." msgstr "Mã bài đã tồn tại." -#: judge/forms.py:482 judge/models/contest.py:95 +#: judge/forms.py:491 judge/models/contest.py:102 msgid "Contest id must be ^[a-z0-9]+$" msgstr "Mã kỳ thi phải có dạng ^[a-z0-9]+$" -#: judge/forms.py:489 templates/contest/clone.html:47 -#: templates/problem/search-form.html:39 +#: judge/forms.py:498 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:497 +#: judge/forms.py:506 msgid "Contest with key already exists." msgstr "Mã kỳ thi đã tồn tại." -#: judge/forms.py:505 +#: judge/forms.py:514 msgid "Group doesn't exist." msgstr "Nhóm không tồn tại." -#: judge/forms.py:507 +#: judge/forms.py:516 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:557 +#: judge/forms.py:566 msgid "This problem is duplicated." msgstr "Bài này bị lặp" @@ -591,16 +614,11 @@ 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:33 +#: templates/comments/content-list.html:28 #, python-brace-format msgid "{time}" msgstr "{time}" -#: judge/jinja2/datetime.py:26 templates/blog/content.html:12 -#, python-brace-format -msgid "on {time}" -msgstr "vào {time}" - #: judge/middleware.py:135 msgid "No permission" msgstr "Không có quyền truy cập" @@ -613,209 +631,178 @@ msgstr "Bạn phải là thành viên của nhóm." msgid "No such group" msgstr "Nhóm không tồn tại" -#: judge/models/bookmark.py:16 judge/models/comment.py:171 +#: judge/models/bookmark.py:17 judge/models/comment.py:164 #: judge/models/pagevote.py:16 msgid "associated page" msgstr "trang tương ứng" -#: judge/models/bookmark.py:19 judge/models/comment.py:48 -#: judge/models/pagevote.py:19 judge/models/problem.py:718 +#: 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:32 -#, fuzzy -#| msgid "Bookmark" +#: judge/models/bookmark.py:30 msgid "bookmark" msgstr "Lưu" -#: judge/models/bookmark.py:33 -#, fuzzy -#| msgid "Bookmark" +#: judge/models/bookmark.py:31 msgid "bookmarks" msgstr "Lưu" -#: judge/models/bookmark.py:54 -#, fuzzy -#| msgid "Bookmark" +#: judge/models/bookmark.py:52 msgid "make bookmark" msgstr "Lưu" -#: judge/models/bookmark.py:55 -#, fuzzy -#| msgid "Bookmark" +#: judge/models/bookmark.py:53 msgid "make bookmarks" msgstr "Lưu" -#: 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:43 +#: judge/models/comment.py:44 msgid "commenter" msgstr "người bình luận" -#: judge/models/comment.py:50 +#: judge/models/comment.py:51 msgid "hide the comment" msgstr "ẩn bình luận" -#: judge/models/comment.py:53 +#: judge/models/comment.py:54 msgid "parent" msgstr "" -#: judge/models/comment.py:63 judge/models/notification.py:31 +#: judge/models/comment.py:65 judge/models/notification.py:31 msgid "comment" msgstr "bình luận" -#: judge/models/comment.py:64 +#: judge/models/comment.py:66 msgid "comments" msgstr "" -#: judge/models/comment.py:132 -#, fuzzy -#| msgid "Editorial for {0}" +#: judge/models/comment.py:125 msgid "Editorial for " msgstr "Hướng dẫn cho {0}" -#: judge/models/comment.py:164 +#: judge/models/comment.py:157 msgid "comment vote" msgstr "" -#: judge/models/comment.py:165 +#: judge/models/comment.py:158 msgid "comment votes" msgstr "" -#: judge/models/comment.py:176 +#: judge/models/comment.py:169 msgid "Override comment lock" msgstr "" -#: judge/models/contest.py:42 +#: judge/models/contest.py:49 msgid "Invalid colour." msgstr "" -#: judge/models/contest.py:46 +#: judge/models/contest.py:53 msgid "tag name" msgstr "" -#: judge/models/contest.py:50 +#: judge/models/contest.py:57 msgid "Lowercase letters and hyphens only." msgstr "" -#: judge/models/contest.py:55 +#: judge/models/contest.py:62 msgid "tag colour" msgstr "" -#: judge/models/contest.py:57 +#: judge/models/contest.py:64 msgid "tag description" msgstr "" -#: judge/models/contest.py:78 +#: judge/models/contest.py:85 msgid "contest tag" msgstr "" -#: judge/models/contest.py:79 judge/models/contest.py:255 +#: judge/models/contest.py:86 judge/models/contest.py:262 msgid "contest tags" msgstr "nhãn kỳ thi" -#: judge/models/contest.py:87 +#: judge/models/contest.py:94 msgid "Visible" msgstr "Hiển thị" -#: judge/models/contest.py:88 +#: judge/models/contest.py:95 msgid "Hidden for duration of contest" msgstr "Ẩn trong thời gian kỳ thi" -#: judge/models/contest.py:89 +#: judge/models/contest.py:96 msgid "Hidden for duration of participation" msgstr "Ẩn trong thời gian tham gia" -#: judge/models/contest.py:93 +#: judge/models/contest.py:100 msgid "contest id" msgstr "ID kỳ thi" -#: judge/models/contest.py:98 +#: judge/models/contest.py:105 msgid "contest name" msgstr "tên kỳ thi" -#: judge/models/contest.py:102 judge/models/interface.py:79 -#: judge/models/problem.py:672 +#: judge/models/contest.py:109 judge/models/interface.py:80 +#: judge/models/problem.py:694 msgid "authors" msgstr "tác giả" -#: judge/models/contest.py:103 +#: judge/models/contest.py:110 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:108 judge/models/problem.py:154 +#: judge/models/contest.py:115 judge/models/problem.py:157 msgid "curators" msgstr "quản lý" -#: judge/models/contest.py:110 +#: judge/models/contest.py:117 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:118 judge/models/problem.py:164 +#: judge/models/contest.py:125 judge/models/problem.py:167 msgid "testers" msgstr "" -#: judge/models/contest.py:120 +#: judge/models/contest.py:127 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:125 judge/models/course.py:158 -#: judge/models/runtime.py:211 +#: judge/models/contest.py:132 judge/models/runtime.py:217 msgid "description" msgstr "mô tả" -#: judge/models/contest.py:127 judge/models/problem.py:588 -#: judge/models/runtime.py:216 +#: judge/models/contest.py:134 judge/models/problem.py:610 +#: judge/models/runtime.py:222 msgid "problems" msgstr "bài tập" -#: judge/models/contest.py:129 judge/models/contest.py:652 +#: judge/models/contest.py:136 judge/models/contest.py:698 msgid "start time" msgstr "thời gian bắt đầu" -#: judge/models/contest.py:130 +#: judge/models/contest.py:137 msgid "end time" msgstr "thời gian kết thúc" -#: judge/models/contest.py:132 judge/models/problem.py:183 -#: judge/models/problem.py:623 +#: judge/models/contest.py:139 judge/models/problem.py:186 +#: judge/models/problem.py:645 msgid "time limit" msgstr "giới hạn thời gian" -#: judge/models/contest.py:136 +#: judge/models/contest.py:143 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:140 +#: judge/models/contest.py:147 msgid "freeze after" msgstr "đóng băng sau" -#: judge/models/contest.py:144 +#: judge/models/contest.py:151 msgid "" "Format hh:mm:ss. For example, if you want to freeze contest after 2 hours, " "enter 02:00:00" @@ -823,12 +810,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:148 judge/models/course.py:28 -#: judge/models/course.py:164 judge/models/problem.py:222 +#: judge/models/contest.py:155 judge/models/course.py:27 +#: judge/models/problem.py:225 msgid "publicly visible" msgstr "công khai" -#: judge/models/contest.py:151 +#: judge/models/contest.py:158 msgid "" "Should be set even for organization-private contests, where it determines " "whether the contest is visible to members of the specified organizations." @@ -836,92 +823,92 @@ msgstr "" "Đánh dấu ngay cả với các kỳ thi riêng tư của nhóm, quyết định việc kỳ thi có " "được hiển thị với các thành viên hay không." -#: judge/models/contest.py:157 +#: judge/models/contest.py:164 msgid "contest rated" msgstr "kỳ thi được xếp hạng" -#: judge/models/contest.py:158 +#: judge/models/contest.py:165 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:162 +#: judge/models/contest.py:169 msgid "scoreboard visibility" msgstr "khả năng hiển thị của bảng điểm" -#: judge/models/contest.py:165 +#: judge/models/contest.py:172 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:170 +#: judge/models/contest.py:177 msgid "view contest scoreboard" msgstr "xem bảng điểm kỳ thi" -#: judge/models/contest.py:173 +#: judge/models/contest.py:180 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:176 +#: judge/models/contest.py:183 msgid "public scoreboard" msgstr "công khai bảng điểm" -#: judge/models/contest.py:177 +#: judge/models/contest.py:184 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:181 +#: judge/models/contest.py:188 msgid "no comments" msgstr "không bình luận" -#: judge/models/contest.py:182 +#: judge/models/contest.py:189 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:187 +#: judge/models/contest.py:194 msgid "Rating floor for contest" msgstr "Cận dưới rating được xếp hạng trong kỳ thi" -#: judge/models/contest.py:193 +#: judge/models/contest.py:200 msgid "Rating ceiling for contest" msgstr "Cận trên rating được xếp hạng trong kỳ thi" -#: judge/models/contest.py:198 +#: judge/models/contest.py:205 msgid "rate all" msgstr "xếp hạng tất cả" -#: judge/models/contest.py:199 +#: judge/models/contest.py:206 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:204 +#: judge/models/contest.py:211 msgid "exclude from ratings" msgstr "không xếp hạng" -#: judge/models/contest.py:209 +#: judge/models/contest.py:216 msgid "private to specific users" msgstr "riêng tư với các người dùng này" -#: judge/models/contest.py:214 +#: judge/models/contest.py:221 msgid "private contestants" msgstr "thí sinh riêng tư" -#: judge/models/contest.py:215 +#: judge/models/contest.py:222 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:219 +#: judge/models/contest.py:226 msgid "hide problem tags" msgstr "ẩn nhãn kỳ thi" -#: judge/models/contest.py:220 +#: judge/models/contest.py:227 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:224 +#: judge/models/contest.py:231 msgid "run pretests only" msgstr "chỉ chạy pretests" -#: judge/models/contest.py:226 +#: judge/models/contest.py:233 msgid "" "Whether judges should grade pretests only, versus all testcases. Commonly " "set during a contest, then unset prior to rejudging user submissions when " @@ -930,51 +917,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:233 judge/models/interface.py:96 -#: judge/models/problem.py:282 +#: judge/models/contest.py:240 judge/models/interface.py:97 +#: judge/models/problem.py:285 msgid "private to organizations" msgstr "riêng tư với các tổ chức" -#: judge/models/contest.py:238 judge/models/course.py:34 -#: judge/models/interface.py:92 judge/models/problem.py:278 -#: judge/models/profile.py:149 +#: judge/models/contest.py:245 judge/models/course.py:33 +#: judge/models/interface.py:93 judge/models/problem.py:281 +#: judge/models/profile.py:168 msgid "organizations" msgstr "tổ chức" -#: judge/models/contest.py:239 +#: judge/models/contest.py:246 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:242 judge/models/problem.py:253 +#: judge/models/contest.py:249 judge/models/problem.py:256 msgid "OpenGraph image" msgstr "Hình ảnh OpenGraph" -#: judge/models/contest.py:245 judge/models/profile.py:97 +#: judge/models/contest.py:252 judge/models/profile.py:108 msgid "Logo override image" msgstr "Hình ảnh ghi đè logo" -#: judge/models/contest.py:250 +#: judge/models/contest.py:257 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:258 +#: judge/models/contest.py:265 msgid "the amount of live participants" msgstr "số lượng thí sinh thi trực tiếp" -#: judge/models/contest.py:262 +#: judge/models/contest.py:269 msgid "contest summary" msgstr "tổng kết kỳ thi" -#: judge/models/contest.py:264 judge/models/problem.py:259 +#: judge/models/contest.py:271 judge/models/problem.py:262 msgid "Plain-text, shown in meta description tag, e.g. for social media." msgstr "" -#: judge/models/contest.py:268 judge/models/profile.py:92 +#: judge/models/contest.py:275 judge/models/profile.py:103 msgid "access code" msgstr "mật khẩu truy cập" -#: judge/models/contest.py:273 +#: judge/models/contest.py:280 msgid "" "An optional code to prompt contestants before they are allowed to join the " "contest. Leave it blank to disable." @@ -982,457 +969,478 @@ 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:279 judge/models/problem.py:241 +#: judge/models/contest.py:286 judge/models/problem.py:244 msgid "personae non gratae" msgstr "Chặn tham gia" -#: judge/models/contest.py:281 +#: judge/models/contest.py:288 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:284 +#: judge/models/contest.py:291 msgid "contest format" msgstr "format kỳ thi" -#: judge/models/contest.py:288 +#: judge/models/contest.py:295 msgid "The contest format module to use." msgstr "Format kỳ thi sử dụng." -#: judge/models/contest.py:291 +#: judge/models/contest.py:298 msgid "contest format configuration" msgstr "Tùy chỉnh format kỳ thi" -#: judge/models/contest.py:295 +#: judge/models/contest.py:302 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:308 +#: judge/models/contest.py:315 msgid "precision points" msgstr "Hiển thị điểm" -#: judge/models/contest.py:311 +#: judge/models/contest.py:318 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:620 +#: judge/models/contest.py:321 +msgid "rate limit" +msgstr "giới hạn bài nộp" + +#: judge/models/contest.py:326 +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:652 msgid "See private contests" msgstr "" -#: judge/models/contest.py:621 +#: judge/models/contest.py:653 msgid "Edit own contests" msgstr "" -#: judge/models/contest.py:622 +#: judge/models/contest.py:654 msgid "Edit all contests" msgstr "" -#: judge/models/contest.py:623 +#: judge/models/contest.py:655 msgid "Clone contest" msgstr "" -#: judge/models/contest.py:624 templates/contest/moss.html:72 +#: judge/models/contest.py:656 templates/contest/moss.html:72 msgid "MOSS contest" msgstr "" -#: judge/models/contest.py:625 +#: judge/models/contest.py:657 msgid "Rate contests" msgstr "" -#: judge/models/contest.py:626 +#: judge/models/contest.py:658 msgid "Contest access codes" msgstr "" -#: judge/models/contest.py:627 +#: judge/models/contest.py:659 msgid "Create private contests" msgstr "" -#: judge/models/contest.py:628 +#: judge/models/contest.py:660 msgid "Change contest visibility" msgstr "" -#: judge/models/contest.py:629 +#: judge/models/contest.py:661 msgid "Edit contest problem label script" msgstr "Cách hiển thị thứ tự bài tập" -#: judge/models/contest.py:631 judge/models/contest.py:778 -#: judge/models/contest.py:856 judge/models/contest.py:886 -#: judge/models/course.py:178 judge/models/submission.py:116 +#: judge/models/contest.py:663 judge/models/contest.py:824 +#: judge/models/contest.py:902 judge/models/contest.py:932 +#: judge/models/contest.py:1011 judge/models/submission.py:116 msgid "contest" msgstr "kỳ thi" -#: judge/models/contest.py:632 +#: judge/models/contest.py:664 msgid "contests" msgstr "kỳ thi" -#: judge/models/contest.py:641 +#: judge/models/contest.py:687 msgid "associated contest" msgstr "" -#: judge/models/contest.py:654 +#: judge/models/contest.py:700 msgid "score" msgstr "điểm" -#: judge/models/contest.py:655 +#: judge/models/contest.py:701 msgid "cumulative time" msgstr "tổng thời gian" -#: judge/models/contest.py:657 +#: judge/models/contest.py:703 msgid "is disqualified" msgstr "đã bị loại" -#: judge/models/contest.py:659 +#: judge/models/contest.py:705 msgid "Whether this participation is disqualified." msgstr "Quyết định thí sinh có bị loại không." -#: judge/models/contest.py:661 +#: judge/models/contest.py:707 msgid "tie-breaking field" msgstr "" -#: judge/models/contest.py:663 +#: judge/models/contest.py:709 msgid "virtual participation id" msgstr "id lần tham gia ảo" -#: judge/models/contest.py:665 +#: judge/models/contest.py:711 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:668 +#: judge/models/contest.py:714 msgid "contest format specific data" msgstr "" -#: judge/models/contest.py:671 +#: judge/models/contest.py:717 msgid "same as format_data, but including frozen results" msgstr "" -#: judge/models/contest.py:675 -#, fuzzy -#| msgid "score" +#: judge/models/contest.py:721 msgid "final score" msgstr "điểm" -#: judge/models/contest.py:677 -#, fuzzy -#| msgid "cumulative time" +#: judge/models/contest.py:723 msgid "final cumulative time" msgstr "tổng thời gian" -#: judge/models/contest.py:753 +#: judge/models/contest.py:799 #, python-format msgid "%s spectating in %s" msgstr "%s đang theo dõi trong %s" -#: judge/models/contest.py:758 +#: judge/models/contest.py:804 #, python-format msgid "%s in %s, v%d" msgstr "%s trong %s, v%d" -#: judge/models/contest.py:763 +#: judge/models/contest.py:809 #, python-format msgid "%s in %s" msgstr "%s trong %s" -#: judge/models/contest.py:766 +#: judge/models/contest.py:812 msgid "contest participation" msgstr "lần tham gia kỳ thi" -#: judge/models/contest.py:767 +#: judge/models/contest.py:813 msgid "contest participations" msgstr "lần tham gia kỳ thi" -#: judge/models/contest.py:774 judge/models/contest.py:827 -#: judge/models/contest.py:889 judge/models/problem.py:587 -#: judge/models/problem.py:594 judge/models/problem.py:615 -#: judge/models/problem.py:646 judge/models/problem_data.py:50 +#: judge/models/contest.py:820 judge/models/contest.py:873 +#: judge/models/contest.py:935 judge/models/course.py:165 +#: 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 msgid "problem" msgstr "bài tập" -#: judge/models/contest.py:782 judge/models/contest.py:839 -#: judge/models/course.py:182 judge/models/problem.py:206 +#: judge/models/contest.py:828 judge/models/contest.py:885 +#: judge/models/course.py:167 judge/models/problem.py:209 msgid "points" msgstr "điểm" -#: judge/models/contest.py:783 +#: judge/models/contest.py:829 msgid "partial" msgstr "thành phần" -#: judge/models/contest.py:784 judge/models/contest.py:841 +#: judge/models/contest.py:830 judge/models/contest.py:887 msgid "is pretested" msgstr "dùng pretest" -#: judge/models/contest.py:785 judge/models/interface.py:47 +#: judge/models/contest.py:831 judge/models/course.py:166 +#: judge/models/interface.py:48 msgid "order" msgstr "thứ tự" -#: judge/models/contest.py:787 +#: judge/models/contest.py:833 msgid "visible testcases" msgstr "hiển thị test" -#: judge/models/contest.py:792 +#: judge/models/contest.py:838 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:794 +#: judge/models/contest.py:840 msgid "max submissions" msgstr "số lần nộp tối đa" -#: judge/models/contest.py:797 +#: judge/models/contest.py:843 msgid "Why include a problem you can't submit to?" msgstr "" -#: judge/models/contest.py:801 +#: judge/models/contest.py:847 #, 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:802 -#, fuzzy -#| msgid "frozen subtasks" +#: judge/models/contest.py:848 msgid "hidden subtasks" msgstr "Đóng băng subtasks" -#: judge/models/contest.py:814 +#: judge/models/contest.py:860 msgid "contest problem" msgstr "bài trong kỳ thi" -#: judge/models/contest.py:815 +#: judge/models/contest.py:861 msgid "contest problems" msgstr "bài trong kỳ thi" -#: judge/models/contest.py:821 judge/models/submission.py:273 +#: judge/models/contest.py:867 judge/models/submission.py:274 msgid "submission" msgstr "bài nộp" -#: judge/models/contest.py:834 judge/models/contest.py:860 +#: judge/models/contest.py:880 judge/models/contest.py:906 msgid "participation" msgstr "lần tham gia" -#: judge/models/contest.py:842 +#: judge/models/contest.py:888 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:847 +#: judge/models/contest.py:893 msgid "contest submission" msgstr "bài nộp kỳ thi" -#: judge/models/contest.py:848 +#: judge/models/contest.py:894 msgid "contest submissions" msgstr "bài nộp kỳ thi" -#: judge/models/contest.py:864 +#: judge/models/contest.py:910 msgid "rank" msgstr "rank" -#: judge/models/contest.py:865 +#: judge/models/contest.py:911 msgid "rating" msgstr "rating" -#: judge/models/contest.py:866 +#: judge/models/contest.py:912 msgid "raw rating" msgstr "rating thật" -#: judge/models/contest.py:867 +#: judge/models/contest.py:913 msgid "contest performance" msgstr "" -#: judge/models/contest.py:868 +#: judge/models/contest.py:914 msgid "last rated" msgstr "lần cuối được xếp hạng" -#: judge/models/contest.py:872 +#: judge/models/contest.py:918 msgid "contest rating" msgstr "rating kỳ thi" -#: judge/models/contest.py:873 +#: judge/models/contest.py:919 msgid "contest ratings" msgstr "rating kỳ thi" -#: judge/models/contest.py:897 +#: judge/models/contest.py:943 msgid "contest moss result" msgstr "kết quả MOSS kỳ thi" -#: judge/models/contest.py:898 +#: judge/models/contest.py:944 msgid "contest moss results" msgstr "kết quả MOSS kỳ thi" -#: judge/models/contest.py:903 +#: judge/models/contest.py:949 msgid "clarified problem" msgstr "" -#: judge/models/contest.py:905 +#: judge/models/contest.py:951 msgid "clarification body" msgstr "" -#: judge/models/contest.py:907 +#: judge/models/contest.py:953 msgid "clarification timestamp" msgstr "" -#: judge/models/contest.py:925 -#, fuzzy -#| msgid "contest summary" +#: judge/models/contest.py:972 msgid "contests summary" msgstr "tổng kết kỳ thi" -#: judge/models/contest.py:926 -#, fuzzy -#| msgid "contest summary" +#: judge/models/contest.py:973 msgid "contests summaries" msgstr "tổng kết kỳ thi" -#: judge/models/course.py:21 -#, fuzzy -#| msgid "username" -msgid "course name" -msgstr "tên đăng nhập" +#: judge/models/contest.py:984 judge/models/contest.py:991 +msgid "official contest category" +msgstr "loại kỳ thi chính thức" -#: judge/models/course.py:23 judge/models/profile.py:58 -msgid "organization description" -msgstr "mô tả tổ chức" +#: judge/models/contest.py:992 +msgid "official contest categories" +msgstr "các loại kỳ thi chính thức" + +#: judge/models/contest.py:997 judge/models/contest.py:1004 +msgid "official contest location" +msgstr "địa điểm kỳ thi chính thức" + +#: judge/models/contest.py:1005 +msgid "official contest locations" +msgstr "các địa điểm kỳ thi chính thức" + +#: judge/models/contest.py:1017 +msgid "contest category" +msgstr "loại kỳ thi" + +#: judge/models/contest.py:1020 +msgid "year" +msgstr "năm" + +#: judge/models/contest.py:1023 +msgid "contest location" +msgstr "địa điểm kỳ thi" + +#: judge/models/contest.py:1028 +msgid "official contest" +msgstr "kỳ thi chính thức" + +#: judge/models/contest.py:1029 +msgid "official contests" +msgstr "các kỳ thi chính thức" + +#: judge/models/course.py:12 templates/course/grades.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 +msgid "course name" +msgstr "tên khóa học" #: judge/models/course.py:25 -#, fuzzy -#| msgid "end time" -msgid "ending time" -msgstr "thời gian kết thúc" +msgid "course description" +msgstr "Mô tả khóa học" -#: judge/models/course.py:35 -#, fuzzy -#| msgid "If private, only these organizations may see the contest" +#: judge/models/course.py:34 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 kỳ thi" +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" #: 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:43 judge/models/profile.py:50 +#: judge/models/course.py:42 judge/models/profile.py:59 msgid "Only alphanumeric and hyphens" -msgstr "" +msgstr "Chỉ chứa chữ cái và dấu gạch ngang (-)" -#: judge/models/course.py:47 -#, fuzzy -#| msgid "Registration" +#: judge/models/course.py:46 msgid "public registration" -msgstr "Đăng ký" +msgstr "Cho phép đăng ký" -#: judge/models/course.py:51 +#: judge/models/course.py:50 msgid "course image" -msgstr "" +msgstr "hình ảnh khóa học" -#: judge/models/course.py:109 judge/models/course.py:147 -#: judge/models/course.py:172 +#: judge/models/course.py:123 judge/models/course.py:159 msgid "course" -msgstr "" +msgstr "khóa học" -#: judge/models/course.py:117 -msgid "user_of_course" -msgstr "" +#: judge/models/course.py:163 +msgid "course title" +msgstr "tiêu đề khóa học" -#: judge/models/course.py:121 -msgid "Student" -msgstr "" +#: judge/models/course.py:164 +msgid "course content" +msgstr "nội dung khóa học" -#: judge/models/course.py:122 -msgid "Assistant" -msgstr "" - -#: judge/models/course.py:123 -msgid "Teacher" -msgstr "" - -#: judge/models/course.py:152 -#, fuzzy -#| msgid "user profiles" -msgid "course files" -msgstr "thông tin người dùng" - -#: judge/models/interface.py:28 +#: judge/models/interface.py:29 msgid "configuration item" msgstr "" -#: judge/models/interface.py:29 +#: judge/models/interface.py:30 msgid "miscellaneous configuration" msgstr "" -#: judge/models/interface.py:41 +#: judge/models/interface.py:42 msgid "navigation item" msgstr "mục điều hướng" -#: judge/models/interface.py:42 +#: judge/models/interface.py:43 msgid "navigation bar" msgstr "thanh điều hướng" -#: judge/models/interface.py:48 +#: judge/models/interface.py:49 msgid "identifier" msgstr "" -#: judge/models/interface.py:49 +#: judge/models/interface.py:50 msgid "label" msgstr "nhãn" -#: judge/models/interface.py:52 +#: judge/models/interface.py:53 msgid "highlight regex" msgstr "" -#: judge/models/interface.py:56 +#: judge/models/interface.py:57 msgid "parent item" msgstr "mục cha" -#: judge/models/interface.py:78 +#: judge/models/interface.py:79 msgid "post title" msgstr "tiêu đề bài đăng" -#: judge/models/interface.py:80 +#: judge/models/interface.py:81 msgid "slug" msgstr "slug" -#: judge/models/interface.py:81 judge/models/problem.py:670 +#: judge/models/interface.py:82 judge/models/problem.py:692 msgid "public visibility" msgstr "khả năng hiển thị công khai" -#: judge/models/interface.py:82 +#: judge/models/interface.py:83 msgid "sticky" msgstr "nổi lên đầu" -#: judge/models/interface.py:83 +#: judge/models/interface.py:84 msgid "publish after" msgstr "đăng sau khi" -#: judge/models/interface.py:84 +#: judge/models/interface.py:85 msgid "post content" msgstr "đăng nội dung" -#: judge/models/interface.py:85 +#: judge/models/interface.py:86 msgid "post summary" msgstr "đăng tổng kết" -#: judge/models/interface.py:87 +#: judge/models/interface.py:88 msgid "openGraph image" msgstr "hình ảnh openGraph" -#: judge/models/interface.py:93 +#: judge/models/interface.py:94 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:136 +#: judge/models/interface.py:141 msgid "Edit all posts" msgstr "Chỉnh sửa tất cả bài đăng" -#: judge/models/interface.py:137 +#: judge/models/interface.py:142 msgid "blog post" msgstr "bài đăng" -#: judge/models/interface.py:138 +#: judge/models/interface.py:143 msgid "blog posts" msgstr "bài đăng" @@ -1492,144 +1500,144 @@ msgstr "bình chọn" msgid "pagevotes" msgstr "bình chọn" -#: judge/models/pagevote.py:51 +#: judge/models/pagevote.py:48 #, fuzzy #| msgid "volunteer vote" msgid "pagevote vote" msgstr "vote từ TNV" -#: judge/models/pagevote.py:52 +#: judge/models/pagevote.py:49 #, fuzzy #| msgid "volunteer votes" msgid "pagevote votes" msgstr "vote từ TNV" -#: judge/models/problem.py:43 +#: judge/models/problem.py:46 msgid "problem category ID" msgstr "mã của nhóm bài" -#: judge/models/problem.py:46 +#: judge/models/problem.py:49 msgid "problem category name" msgstr "tên nhóm bài" -#: judge/models/problem.py:54 +#: judge/models/problem.py:57 msgid "problem type" msgstr "dạng bài" -#: judge/models/problem.py:55 judge/models/problem.py:173 +#: judge/models/problem.py:58 judge/models/problem.py:176 #: judge/models/volunteer.py:28 msgid "problem types" msgstr "dạng bài" -#: judge/models/problem.py:60 +#: judge/models/problem.py:63 msgid "problem group ID" msgstr "mã của nhóm bài" -#: judge/models/problem.py:62 +#: judge/models/problem.py:65 msgid "problem group name" msgstr "tên nhóm bài" -#: judge/models/problem.py:69 judge/models/problem.py:178 +#: judge/models/problem.py:72 judge/models/problem.py:181 msgid "problem group" msgstr "nhóm bài" -#: judge/models/problem.py:70 +#: judge/models/problem.py:73 msgid "problem groups" msgstr "nhóm bài" -#: judge/models/problem.py:77 +#: judge/models/problem.py:80 msgid "key" msgstr "" -#: judge/models/problem.py:80 +#: judge/models/problem.py:83 msgid "link" msgstr "đường dẫn" -#: judge/models/problem.py:81 +#: judge/models/problem.py:84 msgid "full name" msgstr "tên đầy đủ" -#: judge/models/problem.py:85 judge/models/profile.py:55 -#: judge/models/runtime.py:34 +#: judge/models/problem.py:88 judge/models/profile.py:64 +#: judge/models/runtime.py:35 msgid "short name" msgstr "tên ngắn" -#: judge/models/problem.py:86 +#: judge/models/problem.py:89 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:91 +#: judge/models/problem.py:94 msgid "icon" msgstr "icon" -#: judge/models/problem.py:92 +#: judge/models/problem.py:95 msgid "URL to the icon" msgstr "Đường dẫn icon" -#: judge/models/problem.py:94 +#: judge/models/problem.py:97 msgid "license text" msgstr "văn bản giấy phép" -#: judge/models/problem.py:103 +#: judge/models/problem.py:106 msgid "license" msgstr "" -#: judge/models/problem.py:104 +#: judge/models/problem.py:107 msgid "licenses" msgstr "" -#: judge/models/problem.py:127 +#: judge/models/problem.py:130 msgid "problem code" msgstr "mã bài" -#: judge/models/problem.py:133 +#: judge/models/problem.py:136 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:138 +#: judge/models/problem.py:141 msgid "problem name" msgstr "Tên bài" -#: judge/models/problem.py:140 +#: judge/models/problem.py:143 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:142 +#: judge/models/problem.py:145 msgid "problem body" msgstr "Nội dung" -#: judge/models/problem.py:145 +#: judge/models/problem.py:148 msgid "creators" msgstr "" -#: judge/models/problem.py:149 +#: judge/models/problem.py:152 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:158 +#: judge/models/problem.py:161 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:168 +#: judge/models/problem.py:171 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:174 judge/models/volunteer.py:29 +#: judge/models/problem.py:177 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:180 +#: judge/models/problem.py:183 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:185 +#: judge/models/problem.py:188 msgid "" "The time limit for this problem, in seconds. Fractional seconds (e.g. 1.5) " "are supported." @@ -1637,11 +1645,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:194 judge/models/problem.py:630 +#: judge/models/problem.py:197 judge/models/problem.py:652 msgid "memory limit" msgstr "Giới hạn bộ nhớ" -#: judge/models/problem.py:196 +#: judge/models/problem.py:199 msgid "" "The memory limit for this problem, in kilobytes (e.g. 256mb = 262144 " "kilobytes)." @@ -1649,7 +1657,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:208 +#: judge/models/problem.py:211 msgid "" "Points awarded for problem completion. Points are displayed with a 'p' " "suffix if partial." @@ -1657,148 +1665,148 @@ msgstr "" "Điểm thưởng khi hoàn thành bài tập. Điểm có thêm chữ 'p' ở sau cùng nếu như " "chấp nhận cho điểm thành phần (có điểm ngay khi không đúng toàn bộ test)" -#: judge/models/problem.py:214 +#: judge/models/problem.py:217 msgid "allows partial points" msgstr "cho phép điểm thành phần" -#: judge/models/problem.py:218 +#: judge/models/problem.py:221 msgid "allowed languages" msgstr "các ngôn ngữ được cho phép" -#: judge/models/problem.py:219 +#: judge/models/problem.py:222 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:225 +#: judge/models/problem.py:228 msgid "manually managed" msgstr "" -#: judge/models/problem.py:228 +#: judge/models/problem.py:231 msgid "Whether judges should be allowed to manage data or not." msgstr "" -#: judge/models/problem.py:231 +#: judge/models/problem.py:234 msgid "date of publishing" msgstr "Ngày công bố" -#: judge/models/problem.py:236 +#: judge/models/problem.py:239 msgid "" "Doesn't have magic ability to auto-publish due to backward compatibility" msgstr "" -#: judge/models/problem.py:243 +#: judge/models/problem.py:246 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:250 +#: judge/models/problem.py:253 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:257 +#: judge/models/problem.py:260 msgid "problem summary" msgstr "Tóm tắt bài tập" -#: judge/models/problem.py:263 +#: judge/models/problem.py:266 msgid "number of users" msgstr "" -#: judge/models/problem.py:265 +#: judge/models/problem.py:268 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:267 +#: judge/models/problem.py:270 msgid "solve rate" msgstr "Tỉ lệ giải đúng" -#: judge/models/problem.py:279 +#: judge/models/problem.py:282 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:285 +#: judge/models/problem.py:288 msgid "pdf statement" msgstr "Đề bài bằng file pdf" -#: judge/models/problem.py:599 judge/models/problem.py:620 -#: judge/models/problem.py:651 judge/models/runtime.py:161 +#: judge/models/problem.py:621 judge/models/problem.py:642 +#: judge/models/problem.py:673 judge/models/runtime.py:159 msgid "language" msgstr "" -#: judge/models/problem.py:602 +#: judge/models/problem.py:624 msgid "translated name" msgstr "" -#: judge/models/problem.py:604 +#: judge/models/problem.py:626 msgid "translated description" msgstr "" -#: judge/models/problem.py:608 +#: judge/models/problem.py:630 msgid "problem translation" msgstr "" -#: judge/models/problem.py:609 +#: judge/models/problem.py:631 msgid "problem translations" msgstr "" -#: judge/models/problem.py:639 +#: judge/models/problem.py:661 msgid "language-specific resource limit" msgstr "" -#: judge/models/problem.py:640 +#: judge/models/problem.py:662 msgid "language-specific resource limits" msgstr "" -#: judge/models/problem.py:653 judge/models/submission.py:289 +#: judge/models/problem.py:675 judge/models/submission.py:291 msgid "source code" msgstr "mã nguồn" -#: judge/models/problem.py:657 +#: judge/models/problem.py:679 msgid "language-specific template" msgstr "" -#: judge/models/problem.py:658 +#: judge/models/problem.py:680 msgid "language-specific templates" msgstr "" -#: judge/models/problem.py:665 +#: judge/models/problem.py:687 msgid "associated problem" msgstr "" -#: judge/models/problem.py:671 +#: judge/models/problem.py:693 msgid "publish date" msgstr "" -#: judge/models/problem.py:673 +#: judge/models/problem.py:695 msgid "editorial content" msgstr "nội dung lời giải" -#: judge/models/problem.py:686 +#: judge/models/problem.py:712 #, python-format msgid "Editorial for %s" msgstr "" -#: judge/models/problem.py:690 +#: judge/models/problem.py:716 msgid "solution" msgstr "lời giải" -#: judge/models/problem.py:691 +#: judge/models/problem.py:717 msgid "solutions" msgstr "lời giải" -#: judge/models/problem.py:696 +#: judge/models/problem.py:722 #, fuzzy #| msgid "point value" msgid "proposed point value" msgstr "điểm" -#: judge/models/problem.py:697 +#: judge/models/problem.py:723 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:711 +#: judge/models/problem.py:737 msgid "The time this vote was cast" msgstr "" -#: judge/models/problem.py:717 +#: judge/models/problem.py:743 msgid "vote" msgstr "" @@ -1839,8 +1847,8 @@ msgid "Custom checker (PY)" msgstr "Trình chấm tự viết (Python)" #: judge/models/problem_data.py:41 -msgid "Custom validator (CPP)" -msgstr "Trình chấm tự viết (C++)" +msgid "Custom checker (CPP)" +msgstr "Trình chấm tự viết (CPP)" #: judge/models/problem_data.py:42 msgid "Interactive" @@ -1884,15 +1892,15 @@ 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 "file trình chấm" +msgstr "trình chấm" #: judge/models/problem_data.py:94 -msgid "custom validator file" -msgstr "file trình chấm" +msgid "custom cpp checker file" +msgstr "trình chấm C++" #: judge/models/problem_data.py:102 msgid "interactive judge" -msgstr "" +msgstr "trình chấm interactive" #: judge/models/problem_data.py:110 judge/models/problem_data.py:229 msgid "input file name" @@ -1970,394 +1978,394 @@ msgstr "điểm" msgid "case is pretest?" msgstr "test là pretest?" -#: judge/models/profile.py:43 +#: judge/models/profile.py:52 msgid "organization title" msgstr "tiêu đề tổ chức" -#: judge/models/profile.py:46 +#: judge/models/profile.py:55 msgid "organization slug" msgstr "tên ngắn đường dẫn" -#: judge/models/profile.py:47 +#: judge/models/profile.py:56 msgid "Organization name shown in URL" msgstr "Tên được hiển thị trong đường dẫn" -#: judge/models/profile.py:56 +#: judge/models/profile.py:65 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:61 +#: judge/models/profile.py:68 +msgid "organization description" +msgstr "mô tả tổ chức" + +#: judge/models/profile.py:72 msgid "registrant" msgstr "người tạo" -#: judge/models/profile.py:64 +#: judge/models/profile.py:75 msgid "User who registered this organization" msgstr "Người tạo tổ chức" -#: judge/models/profile.py:68 +#: judge/models/profile.py:79 msgid "administrators" msgstr "người quản lý" -#: judge/models/profile.py:70 +#: judge/models/profile.py:81 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:73 +#: judge/models/profile.py:84 msgid "creation date" msgstr "ngày tạo" -#: judge/models/profile.py:76 +#: judge/models/profile.py:87 msgid "is open organization?" msgstr "tổ chức mở?" -#: judge/models/profile.py:77 +#: judge/models/profile.py:88 msgid "Allow joining organization" msgstr "Cho phép mọi người tham gia tổ chức" -#: judge/models/profile.py:81 +#: judge/models/profile.py:92 msgid "maximum size" msgstr "số lượng thành viên tối đa" -#: judge/models/profile.py:85 +#: judge/models/profile.py:96 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:91 +#: judge/models/profile.py:102 msgid "Student access code" msgstr "Mã truy cập cho học sinh" -#: judge/models/profile.py:102 +#: judge/models/profile.py:113 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:148 judge/models/profile.py:178 -#: judge/models/profile.py:439 judge/models/profile.py:518 +#: judge/models/profile.py:167 judge/models/profile.py:199 +#: judge/models/profile.py:468 judge/models/profile.py:543 msgid "organization" msgstr "" -#: judge/models/profile.py:155 +#: judge/models/profile.py:174 msgid "user associated" msgstr "" -#: judge/models/profile.py:157 +#: judge/models/profile.py:177 msgid "self-description" msgstr "" -#: judge/models/profile.py:160 +#: judge/models/profile.py:181 msgid "location" msgstr "" -#: judge/models/profile.py:166 +#: judge/models/profile.py:187 msgid "preferred language" msgstr "" -#: judge/models/profile.py:174 +#: judge/models/profile.py:195 msgid "last access time" msgstr "" -#: judge/models/profile.py:175 +#: judge/models/profile.py:196 msgid "last IP" msgstr "" -#: judge/models/profile.py:186 +#: judge/models/profile.py:207 msgid "display rank" msgstr "" -#: judge/models/profile.py:195 +#: judge/models/profile.py:216 msgid "comment mute" msgstr "" -#: judge/models/profile.py:196 +#: judge/models/profile.py:217 msgid "Some users are at their best when silent." msgstr "" -#: judge/models/profile.py:200 +#: judge/models/profile.py:221 msgid "unlisted user" msgstr "" -#: judge/models/profile.py:201 +#: judge/models/profile.py:222 msgid "User will not be ranked." msgstr "" -#: judge/models/profile.py:205 -#, fuzzy -#| msgid "Banned from joining" -msgid "banned from voting" -msgstr "Bị cấm tham gia" - -#: judge/models/profile.py:206 -msgid "User will not be able to vote on problems' point values." -msgstr "" - -#: judge/models/profile.py:211 -msgid "user script" -msgstr "" - -#: judge/models/profile.py:215 -msgid "User-defined JavaScript for site customization." -msgstr "" - -#: judge/models/profile.py:219 +#: judge/models/profile.py:228 msgid "current contest" msgstr "kỳ thi hiện tại" -#: judge/models/profile.py:226 -msgid "math engine" -msgstr "" - -#: judge/models/profile.py:230 -msgid "the rendering engine used to render math" -msgstr "" - -#: judge/models/profile.py:233 +#: judge/models/profile.py:235 msgid "2FA enabled" msgstr "" -#: judge/models/profile.py:235 +#: judge/models/profile.py:237 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:241 +#: judge/models/profile.py:243 msgid "TOTP key" msgstr "mã TOTP" -#: judge/models/profile.py:242 +#: judge/models/profile.py:244 msgid "32 character base32-encoded key for TOTP" msgstr "" -#: judge/models/profile.py:244 +#: judge/models/profile.py:246 msgid "TOTP key must be empty or base32" msgstr "" -#: judge/models/profile.py:248 +#: judge/models/profile.py:250 msgid "internal notes" msgstr "ghi chú nội bộ" -#: judge/models/profile.py:251 +#: judge/models/profile.py:253 msgid "Notes for administrators regarding this user." msgstr "Ghi chú riêng cho quản trị viên." -#: judge/models/profile.py:256 +#: judge/models/profile.py:258 msgid "Custom background" msgstr "Background tự chọn" -#: judge/models/profile.py:259 +#: judge/models/profile.py:261 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:426 +#: judge/models/profile.py:425 msgid "user profile" msgstr "thông tin người dùng" -#: judge/models/profile.py:427 +#: judge/models/profile.py:426 msgid "user profiles" msgstr "thông tin người dùng" -#: judge/models/profile.py:443 +#: judge/models/profile.py:432 +#, fuzzy +#| msgid "associated page" +msgid "profile associated" +msgstr "trang tương ứng" + +#: judge/models/profile.py:439 +msgid "t-shirt size" +msgstr "" + +#: judge/models/profile.py:444 +#, fuzzy +#| msgid "date of publishing" +msgid "date of birth" +msgstr "Ngày công bố" + +#: judge/models/profile.py:450 +msgid "address" +msgstr "" + +#: judge/models/profile.py:472 msgid "request time" msgstr "thời gian đăng ký" -#: judge/models/profile.py:446 +#: judge/models/profile.py:475 msgid "state" msgstr "trạng thái" -#: judge/models/profile.py:453 +#: judge/models/profile.py:482 msgid "reason" msgstr "lý do" -#: judge/models/profile.py:456 +#: judge/models/profile.py:485 msgid "organization join request" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:457 +#: judge/models/profile.py:486 msgid "organization join requests" msgstr "đơn đăng ký tham gia" -#: judge/models/profile.py:523 +#: judge/models/profile.py:548 #, fuzzy #| msgid "last seen" msgid "last visit" msgstr "xem lần cuối" -#: judge/models/runtime.py:21 +#: judge/models/runtime.py:22 msgid "short identifier" msgstr "tên ngắn" -#: judge/models/runtime.py:23 +#: judge/models/runtime.py:24 msgid "" "The identifier for this language; the same as its executor id for judges." msgstr "" -#: judge/models/runtime.py:29 +#: judge/models/runtime.py:30 msgid "long name" msgstr "tên dài" -#: judge/models/runtime.py:30 +#: judge/models/runtime.py:31 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:36 +#: judge/models/runtime.py:37 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:45 +#: judge/models/runtime.py:46 msgid "common name" msgstr "" -#: judge/models/runtime.py:47 +#: judge/models/runtime.py:48 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:53 +#: judge/models/runtime.py:54 msgid "ace mode name" msgstr "" -#: judge/models/runtime.py:55 +#: judge/models/runtime.py:56 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:61 +#: judge/models/runtime.py:62 msgid "pygments name" msgstr "" -#: judge/models/runtime.py:62 +#: judge/models/runtime.py:63 msgid "Language ID for Pygments highlighting in source windows." msgstr "" -#: judge/models/runtime.py:65 +#: judge/models/runtime.py:66 msgid "code template" msgstr "" -#: judge/models/runtime.py:66 +#: judge/models/runtime.py:67 msgid "Code template to display in submission editor." msgstr "" -#: judge/models/runtime.py:71 +#: judge/models/runtime.py:72 msgid "runtime info override" msgstr "" -#: judge/models/runtime.py:74 +#: judge/models/runtime.py:75 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:79 +#: judge/models/runtime.py:80 msgid "language description" msgstr "" -#: judge/models/runtime.py:81 +#: judge/models/runtime.py:82 msgid "" "Use this field to inform users of quirks with your environment, additional " "restrictions, etc." msgstr "" -#: judge/models/runtime.py:88 +#: judge/models/runtime.py:89 msgid "extension" msgstr "" -#: judge/models/runtime.py:89 +#: judge/models/runtime.py:90 msgid "The extension of source files, e.g., \"py\" or \"cpp\"." msgstr "" -#: judge/models/runtime.py:162 +#: judge/models/runtime.py:160 msgid "languages" msgstr "ngôn ngữ" -#: judge/models/runtime.py:168 +#: judge/models/runtime.py:174 msgid "language to which this runtime belongs" msgstr "" -#: judge/models/runtime.py:172 +#: judge/models/runtime.py:178 msgid "judge on which this runtime exists" msgstr "" -#: judge/models/runtime.py:174 +#: judge/models/runtime.py:180 msgid "runtime name" msgstr "" -#: judge/models/runtime.py:176 +#: judge/models/runtime.py:182 msgid "runtime version" msgstr "" -#: judge/models/runtime.py:179 +#: judge/models/runtime.py:185 msgid "order in which to display this runtime" msgstr "" -#: judge/models/runtime.py:185 +#: judge/models/runtime.py:191 msgid "Server name, hostname-style" msgstr "Tên web" -#: judge/models/runtime.py:188 +#: judge/models/runtime.py:194 msgid "time of creation" msgstr "ngày tạo" -#: judge/models/runtime.py:192 +#: judge/models/runtime.py:198 msgid "A key to authenticate this judge" msgstr "Chìa khóa xác thực" -#: judge/models/runtime.py:193 +#: judge/models/runtime.py:199 msgid "authentication key" msgstr "mã xác thực" -#: judge/models/runtime.py:196 +#: judge/models/runtime.py:202 msgid "block judge" msgstr "chặn máy chấm" -#: judge/models/runtime.py:199 +#: judge/models/runtime.py:205 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:203 +#: judge/models/runtime.py:209 msgid "judge online status" msgstr "trạng thái online của máy chấm" -#: judge/models/runtime.py:204 +#: judge/models/runtime.py:210 msgid "judge start time" msgstr "thời gian khởi đầu máy chấm" -#: judge/models/runtime.py:205 +#: judge/models/runtime.py:211 msgid "response time" msgstr "thời gian trả lời" -#: judge/models/runtime.py:207 +#: judge/models/runtime.py:213 msgid "system load" msgstr "lưu lượng xử lý" -#: judge/models/runtime.py:209 +#: judge/models/runtime.py:215 msgid "Load for the last minute, divided by processors to be fair." msgstr "Lưu lượng được chia đều." -#: judge/models/runtime.py:219 judge/models/runtime.py:261 +#: judge/models/runtime.py:225 judge/models/runtime.py:267 msgid "judges" msgstr "máy chấm" -#: judge/models/runtime.py:260 +#: judge/models/runtime.py:266 msgid "judge" msgstr "máy chấm" #: judge/models/submission.py:20 judge/models/submission.py:47 -#: judge/utils/problems.py:114 +#: judge/utils/problems.py:116 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" @@ -2378,7 +2386,7 @@ msgid "Runtime Error" msgstr "Runtime Error" #: judge/models/submission.py:27 judge/models/submission.py:41 -#: judge/models/submission.py:55 judge/utils/problems.py:118 +#: judge/models/submission.py:55 judge/utils/problems.py:124 msgid "Compile Error" msgstr "Compile Error" @@ -2419,15 +2427,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:308 +#: judge/models/submission.py:69 judge/models/submission.py:310 msgid "execution time" msgstr "thời gian chạy" -#: judge/models/submission.py:70 judge/models/submission.py:309 +#: judge/models/submission.py:70 judge/models/submission.py:311 msgid "memory usage" msgstr "bộ nhớ sử dụng" -#: judge/models/submission.py:72 judge/models/submission.py:310 +#: judge/models/submission.py:72 judge/models/submission.py:312 msgid "points granted" msgstr "điểm" @@ -2475,50 +2483,56 @@ msgstr "được chấm lại bởi admin" msgid "was ran on pretests only" msgstr "chỉ chấm pretest" -#: judge/models/submission.py:274 templates/contest/moss.html:56 +#: judge/models/submission.py:275 templates/contest/moss.html:56 msgid "submissions" msgstr "bài nộp" -#: judge/models/submission.py:286 judge/models/submission.py:300 +#: judge/models/submission.py:288 judge/models/submission.py:302 msgid "associated submission" msgstr "bài nộp tương ứng" -#: judge/models/submission.py:304 +#: judge/models/submission.py:306 msgid "test case ID" msgstr "test case ID" -#: judge/models/submission.py:306 +#: judge/models/submission.py:308 msgid "status flag" msgstr "" -#: judge/models/submission.py:311 +#: judge/models/submission.py:313 msgid "points possible" msgstr "" -#: judge/models/submission.py:312 +#: judge/models/submission.py:314 msgid "batch number" msgstr "số thứ tự của nhóm" -#: judge/models/submission.py:314 +#: judge/models/submission.py:316 msgid "judging feedback" msgstr "phản hồi từ máy chấm" -#: judge/models/submission.py:317 +#: judge/models/submission.py:319 msgid "extended judging feedback" msgstr "phản hồi thêm từ máy chấm" -#: judge/models/submission.py:319 +#: judge/models/submission.py:321 msgid "program output" msgstr "output chương trình" -#: judge/models/submission.py:327 +#: judge/models/submission.py:329 msgid "submission test case" msgstr "cái testcase trong bài nộp" -#: judge/models/submission.py:328 +#: judge/models/submission.py:330 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" @@ -2605,6 +2619,22 @@ 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" @@ -2621,75 +2651,67 @@ msgstr "Chỉnh sửa bài nộp" msgid "Recalculating user points" msgstr "Tính lại điểm người dùng" -#: judge/utils/problem_data.py:73 +#: judge/utils/problem_data.py:81 msgid "Empty batches not allowed." msgstr "Nhóm test trống là không hợp lệ." -#: judge/utils/problem_data.py:81 judge/utils/problem_data.py:89 -#: judge/utils/problem_data.py:104 +#: judge/utils/problem_data.py:89 judge/utils/problem_data.py:97 +#: judge/utils/problem_data.py:112 msgid "How did you corrupt the custom checker path?" msgstr "How did you corrupt the custom checker path?" -#: judge/utils/problem_data.py:132 +#: judge/utils/problem_data.py:140 #, 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:139 +#: judge/utils/problem_data.py:147 #, 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:144 +#: judge/utils/problem_data.py:152 #, 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:171 +#: judge/utils/problem_data.py:179 #, 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:194 +#: judge/utils/problem_data.py:202 #, 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:213 +#: judge/utils/problem_data.py:221 msgid "How did you corrupt the zip path?" msgstr "" -#: judge/utils/problem_data.py:219 +#: judge/utils/problem_data.py:227 msgid "How did you corrupt the generator path?" msgstr "" -#: judge/utils/problem_data.py:237 +#: judge/utils/problem_data.py:245 msgid "Invalid interactor judge" msgstr "" -#: judge/utils/problem_data.py:261 +#: judge/utils/problem_data.py:269 #, fuzzy #| msgid "Invalid Return" msgid "Invalid signature handler" msgstr "Invalid Return" -#: judge/utils/problem_data.py:264 +#: judge/utils/problem_data.py:272 msgid "Invalid signature header" msgstr "" -#: judge/utils/problems.py:115 -msgid "Wrong" -msgstr "Sai" - -#: judge/utils/problems.py:121 -msgid "Timeout" -msgstr "Quá thời gian" - -#: judge/utils/problems.py:124 +#: judge/utils/problems.py:134 msgid "Error" msgstr "Lỗi" -#: judge/utils/problems.py:141 +#: judge/utils/problems.py:151 msgid "Can't pass both queryset and keyword filters" msgstr "" @@ -2729,9 +2751,14 @@ msgctxt "hours and minutes" msgid "%h:%m" msgstr "%h:%m" -#: judge/views/about.py:10 templates/organization/home.html:47 -#: templates/organization/org-right-sidebar.html:72 -#: templates/user/user-about.html:72 templates/user/user-tabs.html:4 +#: judge/utils/users.py:61 +msgid "M j, Y" +msgstr "j M, Y" + +#: judge/views/about.py:10 templates/course/course.html:5 +#: templates/organization/home.html:41 +#: templates/organization/org-right-sidebar.html:74 +#: templates/user/user-about.html:70 templates/user/user-tabs.html:4 #: templates/user/users-table.html:22 msgid "About" msgstr "Giới thiệu" @@ -2740,183 +2767,229 @@ msgstr "Giới thiệu" msgid "Custom Checker Sample" msgstr "Hướng dẫn viết trình chấm" -#: judge/views/blog.py:107 +#: judge/views/blog.py:132 #, python-format msgid "Page %d of Posts" msgstr "Trang %d" -#: judge/views/blog.py:149 +#: judge/views/blog.py:172 msgid "Ticket feed" msgstr "Báo cáo" -#: judge/views/blog.py:166 +#: judge/views/blog.py:189 msgid "Comment feed" msgstr "Bình luận" -#: judge/views/comment.py:47 judge/views/pagevote.py:31 +#: judge/views/comment.py:71 judge/views/pagevote.py:32 msgid "Messing around, are we?" msgstr "Messing around, are we?" -#: judge/views/comment.py:63 judge/views/pagevote.py:47 +#: judge/views/comment.py:87 judge/views/pagevote.py:48 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:94 +#: judge/views/comment.py:113 msgid "You already voted." msgstr "Bạn đã vote." -#: judge/views/comment.py:246 judge/views/organization.py:808 -#: judge/views/organization.py:958 judge/views/organization.py:1120 +#: judge/views/comment.py:267 judge/views/organization.py:872 +#: judge/views/organization.py:1022 judge/views/organization.py:1201 msgid "Edited from site" msgstr "Chỉnh sửa từ web" -#: judge/views/comment.py:267 +#: judge/views/comment.py:288 msgid "Editing comment" msgstr "Chỉnh sửa bình luận" -#: judge/views/contests.py:123 judge/views/contests.py:386 -#: judge/views/contests.py:391 judge/views/contests.py:685 +#: judge/views/comment.py:340 +msgid "Comment body" +msgstr "Nội dung bình luận" + +#: judge/views/comment.py:346 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:355 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:398 +msgid "Posted comment" +msgstr "Bình luận đã đăng" + +#: judge/views/contests.py:125 judge/views/contests.py:463 +#: judge/views/contests.py:468 judge/views/contests.py:768 msgid "No such contest" msgstr "Không có contest nào như vậy" -#: judge/views/contests.py:124 judge/views/contests.py:387 +#: judge/views/contests.py:126 judge/views/contests.py:464 #, 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:143 judge/views/stats.py:178 -#: templates/contest/list.html:244 templates/contest/list.html:289 -#: templates/contest/list.html:334 templates/contest/list.html:376 +#: judge/views/contests.py:154 judge/views/contests.py:1555 +#: judge/views/stats.py:178 templates/contest/list.html:170 +#: templates/contest/list.html:212 templates/contest/list.html:249 +#: templates/contest/list.html:283 #: templates/organization/org-left-sidebar.html:5 templates/stats/site.html:21 -#: templates/user/user-bookmarks.html:56 +#: templates/user/user-bookmarks.html:19 templates/user/user-bookmarks.html:80 msgid "Contests" msgstr "Kỳ thi" -#: judge/views/contests.py:391 +#: judge/views/contests.py:324 +msgid "Start time (asc.)" +msgstr "Thời gian bắt đầu (tăng)" + +#: judge/views/contests.py:325 +msgid "Start time (desc.)" +msgstr "Thời gian bắt đầu (giảm)" + +#: judge/views/contests.py:326 judge/views/organization.py:311 +msgid "Name (asc.)" +msgstr "Tên (tăng)" + +#: judge/views/contests.py:327 judge/views/organization.py:312 +msgid "Name (desc.)" +msgstr "Tên (giảm)" + +#: judge/views/contests.py:328 +msgid "User count (asc.)" +msgstr "Số lượng tham gia (tăng)" + +#: judge/views/contests.py:329 +msgid "User count (desc.)" +msgstr "Số lượng tham gia (giảm)" + +#: judge/views/contests.py:468 msgid "Could not find such contest." msgstr "Không tìm thấy kỳ thi nào như vậy." -#: judge/views/contests.py:399 +#: judge/views/contests.py:476 #, 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:468 +#: judge/views/contests.py:554 msgid "Clone Contest" msgstr "Nhân bản kỳ thi" -#: judge/views/contests.py:559 +#: judge/views/contests.py:646 msgid "Contest not ongoing" msgstr "Kỳ thi đang không diễn ra" -#: judge/views/contests.py:560 +#: judge/views/contests.py:647 #, python-format msgid "\"%s\" is not currently ongoing." msgstr "\"%s\" kỳ thi đang không diễn ra." -#: judge/views/contests.py:567 -msgid "Already in contest" -msgstr "Đã ở trong kỳ thi" - -#: judge/views/contests.py:568 -#, python-format -msgid "You are already in a contest: \"%s\"." -msgstr "Bạn đã ở trong kỳ thi: \"%s\"." - -#: judge/views/contests.py:578 +#: judge/views/contests.py:660 msgid "Banned from joining" msgstr "Bị cấm tham gia" -#: judge/views/contests.py:580 +#: judge/views/contests.py:662 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:669 +#: judge/views/contests.py:752 #, python-format msgid "Enter access code for \"%s\"" msgstr "Nhập mật khẩu truy cập cho \"%s\"" -#: judge/views/contests.py:686 +#: judge/views/contests.py:769 #, python-format msgid "You are not in contest \"%s\"." msgstr "Bạn không ở trong kỳ thi \"%s\"." -#: judge/views/contests.py:709 +#: judge/views/contests.py:792 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:767 +#: judge/views/contests.py:850 #, python-format msgid "Contests in %(month)s" msgstr "Các kỳ thi trong %(month)s" -#: judge/views/contests.py:768 +#: judge/views/contests.py:851 msgid "F Y" msgstr "F Y" -#: judge/views/contests.py:828 +#: judge/views/contests.py:911 #, python-format msgid "%s Statistics" msgstr "%s Thống kê" -#: judge/views/contests.py:1124 +#: judge/views/contests.py:1237 #, python-format msgid "%s Rankings" msgstr "%s Bảng điểm" -#: judge/views/contests.py:1135 +#: judge/views/contests.py:1248 msgid "???" msgstr "???" -#: judge/views/contests.py:1162 +#: judge/views/contests.py:1275 #, python-format msgid "Your participation in %s" msgstr "Lần tham gia trong %s" -#: judge/views/contests.py:1163 +#: judge/views/contests.py:1276 #, python-format msgid "%s's participation in %s" msgstr "Lần tham gia của %s trong %s" -#: judge/views/contests.py:1177 +#: judge/views/contests.py:1290 msgid "Live" msgstr "Trực tiếp" -#: judge/views/contests.py:1196 templates/contest/contest-tabs.html:21 +#: judge/views/contests.py:1308 templates/contest/contest-tabs.html:21 msgid "Participation" msgstr "Lần tham gia" -#: judge/views/contests.py:1245 +#: judge/views/contests.py:1357 #, python-format msgid "%s MOSS Results" msgstr "%s Kết quả MOSS" -#: judge/views/contests.py:1281 +#: judge/views/contests.py:1393 #, python-format msgid "Running MOSS for %s..." msgstr "Đang chạy MOSS cho %s..." -#: judge/views/contests.py:1304 +#: judge/views/contests.py:1416 #, python-format msgid "Contest tag: %s" msgstr "Nhãn kỳ thi: %s" -#: judge/views/contests.py:1319 judge/views/ticket.py:67 +#: judge/views/contests.py:1431 judge/views/ticket.py:67 msgid "Issue description" msgstr "Mô tả vấn đề" -#: judge/views/contests.py:1362 +#: judge/views/contests.py:1474 #, python-format msgid "New clarification for %s" msgstr "Thông báo mới cho %s" -#: judge/views/contests.py:1468 -#, fuzzy -#| msgid "contest summary" -msgid "Contests Summary" -msgstr "tổng kết kỳ thi" +#: judge/views/course.py:199 +#, 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:203 +#, 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:242 +#, python-format +msgid "Grades in %(course_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" @@ -2946,7 +3019,7 @@ msgstr "Thay đổi Email" msgid "Change Email" msgstr "Thay đổi Email" -#: judge/views/email.py:83 templates/user/edit-profile.html:120 +#: judge/views/email.py:83 templates/user/edit-profile.html:127 msgid "Change email" msgstr "Thay đổi email" @@ -2981,13 +3054,13 @@ msgstr "không có quyền cho %s" msgid "corrupt page %s" msgstr "trang bị sập %s" -#: judge/views/internal.py:23 +#: judge/views/internal.py:24 #, fuzzy #| msgid "contest problems" msgid "Internal problems" msgstr "bài trong kỳ thi" -#: judge/views/internal.py:83 +#: judge/views/internal.py:106 #, fuzzy #| msgid "request time" msgid "Request times" @@ -3002,103 +3075,115 @@ msgstr "Runtimes" msgid "Markdown Editor" msgstr "" -#: judge/views/notification.py:29 +#: judge/views/notification.py:32 #, python-format msgid "Notifications (%d unseen)" msgstr "Thông báo (%d chưa xem)" -#: judge/views/organization.py:149 judge/views/organization.py:156 +#: judge/views/organization.py:156 judge/views/organization.py:163 msgid "No such organization" msgstr "Không có tổ chức như vậy" -#: judge/views/organization.py:150 +#: judge/views/organization.py:157 #, 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:157 +#: judge/views/organization.py:164 msgid "Could not find such organization." msgstr "" -#: judge/views/organization.py:181 +#: judge/views/organization.py:188 msgid "Can't edit organization" msgstr "Không thể chỉnh sửa tổ chức" -#: judge/views/organization.py:182 +#: judge/views/organization.py:189 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:194 judge/views/organization.py:338 +#: judge/views/organization.py:201 judge/views/organization.py:397 msgid "Can't access organization" msgstr "Không thể truy cập nhóm" -#: judge/views/organization.py:195 judge/views/organization.py:339 +#: judge/views/organization.py:202 judge/views/organization.py:398 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:231 judge/views/stats.py:184 -#: templates/contest/list.html:93 templates/problem/list-base.html:91 +#: judge/views/organization.py:245 judge/views/stats.py:184 +#: templates/contest/list.html:77 templates/problem/list-base.html:90 #: 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:345 +#: judge/views/organization.py:313 +msgid "Member count (asc.)" +msgstr "Số lượng thành viên (tăng)" + +#: judge/views/organization.py:314 +msgid "Member count (desc.)" +msgstr "Số lượng thành viên (giảm)" + +#: judge/views/organization.py:404 #, python-format msgid "%s Members" msgstr "%s Thành viên" -#: judge/views/organization.py:467 +#: judge/views/organization.py:526 #, python-brace-format msgid "All submissions in {0}" msgstr "Bài nộp trong {0}" -#: judge/views/organization.py:497 judge/views/organization.py:503 -#: judge/views/organization.py:510 +#: judge/views/organization.py:534 judge/views/submission.py:857 +msgid "Submissions in" +msgstr "Bài nộp trong" + +#: judge/views/organization.py:559 judge/views/organization.py:565 +#: judge/views/organization.py:572 msgid "Joining group" msgstr "Tham gia nhóm" -#: judge/views/organization.py:498 +#: judge/views/organization.py:560 msgid "You are already in the group." msgstr "Bạn đã ở trong nhóm." -#: judge/views/organization.py:503 +#: judge/views/organization.py:565 msgid "This group is not open." msgstr "Nhóm này là nhóm kín." -#: judge/views/organization.py:511 +#: judge/views/organization.py:573 #, 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:526 +#: judge/views/organization.py:589 msgid "Leaving group" msgstr "Rời nhóm" -#: judge/views/organization.py:527 +#: judge/views/organization.py:590 #, python-format msgid "You are not in \"%s\"." msgstr "Bạn không ở trong \"%s\"." -#: judge/views/organization.py:552 +#: judge/views/organization.py:616 #, python-format msgid "Request to join %s" msgstr "Đăng ký tham gia %s" -#: judge/views/organization.py:582 +#: judge/views/organization.py:646 msgid "Join request detail" msgstr "Chi tiết đơn đăng ký" -#: judge/views/organization.py:624 +#: judge/views/organization.py:688 msgid "Manage join requests" msgstr "Quản lý đơn đăng ký" -#: judge/views/organization.py:628 +#: judge/views/organization.py:692 #, python-format msgid "Managing join requests for %s" msgstr "Quản lý đơn đăng ký cho %s" -#: judge/views/organization.py:668 +#: judge/views/organization.py:732 #, python-format msgid "" "Your organization can only receive %d more members. You cannot approve %d " @@ -3107,163 +3192,170 @@ 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:686 +#: judge/views/organization.py:750 #, python-format msgid "Approved %d user." msgid_plural "Approved %d users." msgstr[0] "Đã chấp thuận %d người." -#: judge/views/organization.py:689 +#: judge/views/organization.py:753 #, python-format msgid "Rejected %d user." msgid_plural "Rejected %d users." msgstr[0] "Đã từ chối %d người." -#: judge/views/organization.py:729 +#: judge/views/organization.py:793 #, python-format msgid "Add member for %s" msgstr "Thêm thành viên cho %s" -#: judge/views/organization.py:741 +#: judge/views/organization.py:805 #, fuzzy #| msgid "Edited from site" msgid "Added members from site" msgstr "Chỉnh sửa từ web" -#: judge/views/organization.py:761 judge/views/organization.py:769 +#: judge/views/organization.py:825 judge/views/organization.py:833 msgid "Can't kick user" msgstr "Không thể đuổi" -#: judge/views/organization.py:762 +#: judge/views/organization.py:826 msgid "The user you are trying to kick does not exist!" msgstr "" -#: judge/views/organization.py:770 +#: judge/views/organization.py:834 #, python-format msgid "The user you are trying to kick is not in organization: %s." msgstr "" -#: judge/views/organization.py:791 judge/views/organization.py:947 +#: judge/views/organization.py:855 judge/views/organization.py:1011 #, python-format msgid "Edit %s" msgstr "Chỉnh sửa %s" -#: judge/views/organization.py:819 templates/organization/list.html:45 +#: judge/views/organization.py:883 templates/organization/search-form.html:19 msgid "Create group" msgstr "Tạo nhóm" -#: judge/views/organization.py:834 +#: judge/views/organization.py:898 msgid "Exceeded limit" msgstr "" -#: judge/views/organization.py:835 +#: judge/views/organization.py:899 #, python-format msgid "You created too many groups. You can only create at most %d groups" msgstr "" -#: judge/views/organization.py:840 judge/views/organization.py:865 -#: judge/views/organization.py:1026 +#: judge/views/organization.py:904 judge/views/organization.py:929 +#: judge/views/organization.py:1102 msgid "Added from site" msgstr "Thêm từ web" -#: judge/views/organization.py:856 -#: templates/organization/org-right-sidebar.html:52 +#: judge/views/organization.py:920 +#: templates/organization/org-right-sidebar.html:47 msgid "Add contest" msgstr "Thêm kỳ thi" -#: judge/views/organization.py:899 judge/views/organization.py:1071 +#: judge/views/organization.py:963 judge/views/organization.py:1152 msgid "Permission denied" msgstr "Truy cập bị từ chối" -#: judge/views/organization.py:900 +#: judge/views/organization.py:964 #, 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:951 templates/blog/blog.html:31 -#: templates/comments/content-list.html:59 -#: templates/comments/content-list.html:73 -#: templates/contest/contest-tabs.html:37 templates/contest/list.html:128 +#: judge/views/organization.py:1015 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/flatpages/admin_link.html:3 #: templates/license.html:10 templates/problem/editorial.html:15 -#: templates/problem/feed/problems.html:50 +#: 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:1015 +#: judge/views/organization.py:1091 #, python-format msgid "Add blog for %s" msgstr "Thêm bài đăng cho %s" -#: judge/views/organization.py:1072 +#: judge/views/organization.py:1153 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:1104 +#: judge/views/organization.py:1185 #, python-format msgid "Edit blog %s" msgstr "Chỉnh sửa %s" -#: judge/views/organization.py:1146 +#: judge/views/organization.py:1232 #, python-format msgid "Pending blogs in %s" msgstr "Bài đang đợi duyệt trong %s" -#: judge/views/problem.py:133 +#: judge/views/problem.py:135 msgid "No such problem" msgstr "Không có bài nào như vậy" -#: judge/views/problem.py:134 +#: judge/views/problem.py:136 #, 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:201 +#: judge/views/problem.py:203 #, python-brace-format msgid "Editorial for {0}" msgstr "Hướng dẫn cho {0}" -#: judge/views/problem.py:205 +#: judge/views/problem.py:207 #, python-brace-format msgid "Editorial for {0}" msgstr "Hướng dẫn cho {0}" -#: judge/views/problem.py:461 templates/contest/contest.html:101 +#: judge/views/problem.py:460 templates/contest/contest.html:112 +#: templates/course/lesson.html:14 #: templates/organization/org-left-sidebar.html:4 -#: templates/user/user-about.html:28 templates/user/user-bookmarks.html:35 -#: templates/user/user-tabs.html:5 templates/user/users-table.html:19 +#: 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 msgid "Problems" msgstr "Bài tập" -#: judge/views/problem.py:834 +#: judge/views/problem.py:842 msgid "Problem feed" msgstr "Bài tập" -#: judge/views/problem.py:1057 +#: judge/views/problem.py:1046 judge/views/problem.py:1079 +msgid "