From ce70006949e8a46e3eed1fb7a3a9c0ec4112503f Mon Sep 17 00:00:00 2001 From: Hieu Nguyen Van Date: Mon, 2 Feb 2026 19:20:20 +0700 Subject: [PATCH 1/2] Revert "Feature/unified view" --- .../commands/generate_editorials.py | 312 +-- judge/migrations/0215_populate_vote_counts.py | 16 +- judge/views/comment.py | 2 +- judge/views/problem.py | 62 - locale/vi/LC_MESSAGES/django.po | 2190 +++++++---------- resources/ace-dmoj.scss | 13 +- resources/base.scss | 12 +- resources/style.scss | 3 +- resources/unified-view.scss | 587 ----- templates/problem/problem.html | 217 +- templates/problem/submit_js.html | 289 --- templates/problem/submit_partial.html | 237 -- templates/problem/unified_view.html | 257 -- 13 files changed, 1115 insertions(+), 3082 deletions(-) delete mode 100644 resources/unified-view.scss delete mode 100644 templates/problem/submit_js.html delete mode 100644 templates/problem/submit_partial.html delete mode 100644 templates/problem/unified_view.html diff --git a/judge/management/commands/generate_editorials.py b/judge/management/commands/generate_editorials.py index 446a4c8cd..70312581d 100644 --- a/judge/management/commands/generate_editorials.py +++ b/judge/management/commands/generate_editorials.py @@ -7,48 +7,50 @@ import logging import time -from typing import List +import json +from typing import List, Optional +from datetime import datetime from django.core.management.base import BaseCommand, CommandError from django.db import transaction from django.utils import timezone -from judge.models import Problem, Solution, Submission +from judge.models import Problem, Solution, Submission, SubmissionSource, Profile try: from pydantic import BaseModel, Field except ImportError: - raise CommandError('Pydantic not installed. Run: pip install pydantic') + raise CommandError("Pydantic not installed. Run: pip install pydantic") # ==================== PYDANTIC MODELS FOR STRUCTURED OUTPUT ==================== class Approach(BaseModel): """Represents a solution approach.""" - name: str = Field(description='Name of the approach (e.g., "Brute Force", "Hash Map", "Two Pointers")') - language: str = Field(description='Programming language used') - code: str = Field(description='Code snippet for this approach') - time_complexity: str = Field(description='Time complexity in Big-O notation (e.g., "O(n)", "O(n log n)")') - space_complexity: str = Field(description='Space complexity in Big-O notation (e.g., "O(1)", "O(n)")') - explanation: str = Field(description='Detailed explanation of how this approach works') + name: str = Field(description="Name of the approach (e.g., 'Brute Force', 'Hash Map', 'Two Pointers')") + language: str = Field(description="Programming language used") + code: str = Field(description="Code snippet for this approach") + time_complexity: str = Field(description="Time complexity in Big-O notation (e.g., 'O(n)', 'O(n log n)')") + space_complexity: str = Field(description="Space complexity in Big-O notation (e.g., 'O(1)', 'O(n)')") + explanation: str = Field(description="Detailed explanation of how this approach works") class EditorialContent(BaseModel): """Structured editorial content.""" problem_understanding: str = Field( - description='Clear explanation of what the problem asks and the key concepts', + description="Clear explanation of what the problem asks and the key concepts" ) approaches: List[Approach] = Field( - description='List of solution approaches, from simplest to most optimal', - min_items=1, + description="List of solution approaches, from simplest to most optimal", + min_items=1 ) key_insights: List[str] = Field( - description='Key insights and patterns to recognize similar problems', - min_items=1, + description="Key insights and patterns to recognize similar problems", + min_items=1 ) common_pitfalls: List[str] = Field( - description='Common mistakes and edge cases to watch out for', - min_items=1, + description="Common mistakes and edge cases to watch out for", + min_items=1 ) @@ -86,25 +88,25 @@ def client(self): if not api_key: raise CommandError( - 'OPENAI_API_KEY environment variable not set. ' - 'Please set it with: export OPENAI_API_KEY="sk-..."', + "OPENAI_API_KEY environment variable not set. " + "Please set it with: export OPENAI_API_KEY='sk-...'" ) if base_url: - self.logger.info('Using custom OpenAI endpoint: %s', base_url) + self.logger.info(f"Using custom OpenAI endpoint: {base_url}") self._client = OpenAI( base_url=base_url, - api_key=api_key, + api_key=api_key ) else: self._client = OpenAI(api_key=api_key) except ImportError: raise CommandError( - 'OpenAI package not installed. Run: pip install openai', + "OpenAI package not installed. Run: pip install openai" ) except Exception as e: - raise CommandError(f'Failed to initialize OpenAI client: {e}') + raise CommandError(f"Failed to initialize OpenAI client: {e}") return self._client def _setup_logging(self, log_file=None): @@ -116,7 +118,7 @@ def _setup_logging(self, log_file=None): logging.basicConfig( level=logging.DEBUG if self.verbose else logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', - handlers=handlers, + handlers=handlers ) self.logger = logging.getLogger(__name__) @@ -138,9 +140,9 @@ def get_ac_solutions(self, problem, limit=3): problem=problem, result='AC', status='D', - language__name__regex=r'^C*$', # C, C++, C++17, C++20, C11, etc. + language__name__regex=r'^C*$' # C, C++, C++17, C++20, C11, etc. ).select_related( - 'user', 'user__user', 'language', 'source', + 'user', 'user__user', 'language', 'source' ).order_by('-id') seen = set() @@ -167,26 +169,26 @@ def validate_generation(self, problem, solutions): errors = [] if not problem: - errors.append('Problem is None') + errors.append("Problem is None") return errors if not problem.is_public: - errors.append(f'Problem {problem.code} is not public') + errors.append(f"Problem {problem.code} is not public") if Solution.objects.filter(problem=problem).exists(): - errors.append(f'Editorial already exists for {problem.code}') + errors.append(f"Editorial already exists for {problem.code}") if len(solutions) < 1: - errors.append(f'Insufficient AC C/C++ solutions for {problem.code}: {len(solutions)} (need at least 1)') + errors.append(f"Insufficient AC C/C++ solutions for {problem.code}: {len(solutions)} (need at least 1)") for i, sub in enumerate(solutions): if not hasattr(sub, 'source') or not sub.source: - errors.append(f'Submission {sub.id} has no source code') + errors.append(f"Submission {sub.id} has no source code") continue source_code = sub.source.source if not source_code or len(source_code.strip()) < 10: - errors.append(f'Submission {sub.id} source code too short') + errors.append(f"Submission {sub.id} source code too short") return errors @@ -194,65 +196,68 @@ def format_solutions_for_prompt(self, solutions): """Format solutions for OpenAI prompt.""" formatted = [] for i, sub in enumerate(solutions, 1): - language = sub.language.common_name if sub.language else 'Unknown' - username = sub.user.user.username if sub.user and sub.user.user else 'Unknown' - source = sub.source.source if sub.source else 'No source' + language = sub.language.common_name if sub.language else "Unknown" + username = sub.user.user.username if sub.user and sub.user.user else "Unknown" + source = sub.source.source if sub.source else "No source" if len(source) > 1000: - source = source[:1000] + '\n// ... (truncated)' + source = source[:1000] + "\n// ... (truncated)" # Normalize language codes for code blocks lang_code = language.lower().replace('c++', 'cpp').replace('c#', 'csharp') formatted.append( - f'--- Solution {i} ---\n' - f'Language: {language}\n' - f'User: {username}\n' - f'Submission ID: {sub.id}\n' - f'Code:\n```{lang_code}\n{source}\n```\n', + f"--- Solution {i} ---\n" + f"Language: {language}\n" + f"User: {username}\n" + f"Submission ID: {sub.id}\n" + f"Code:\n```{lang_code}\n{source}\n```\n" ) - return '\n'.join(formatted) + return "\n".join(formatted) def build_openai_prompt(self, problem, formatted_solutions): """Build the prompt for OpenAI with structured output requirements.""" - description = problem.description or 'No description available' - - prompt = ( - f'You are an expert in competitive programming education. ' - f'Analyze the problem and solutions, then generate a detailed editorial in Vietnamese.\n\n' - f'## Problem Information\n' - f'**Code**: {problem.code}\n' - f'**Name**: {problem.name}\n' - f'**Description**: {description}\n\n' - f'## Accepted Solutions\n' - f'{formatted_solutions}\n\n' - f'## Instructions\n' - f'Analyze the solutions above and create structured editorial data:\n' - f'1. Explain the problem clearly in Vietnamese\n' - f'2. Identify 2-3 different approaches from the solutions\n' - f'3. Order from simplest to most optimal\n' - f'4. Provide code, complexity, and explanation for each approach\n' - f'5. List key insights and common pitfalls\n\n' - f'## Output Requirements\n' - f'You MUST return valid JSON following this schema:\n' - f'{{\n' - f' "problem_understanding": "string - clear explanation in Vietnamese",\n' - f' "approaches": [\n' - f' {{\n' - f' "name": "string - approach name (e.g., \'Brute Force\', \'Hash Map\')",\n' - f' "language": "string - programming language (lowercase, use \'cpp\' for C++, \'csharp\' for C#)",\n' - f' "code": "string - code snippet",\n' - f' "time_complexity": "string - e.g., O(n), O(n log n), can use ~10^9~ if needed",\n' - f' "space_complexity": "string - e.g., O(1), O(n), can use ~10^9~ if needed",\n' - f' "explanation": "string - detailed explanation in Vietnamese"\n' - f' }}\n' - f' ],\n' - f' "key_insights": ["string - insight 1", "string - insight 2"],\n' - f' "common_pitfalls": ["string - pitfall 1", "string - pitfall 2"]\n' - f'}}\n\n' - f'DO NOT include any text outside the JSON object.\n' - ) + description = problem.description or "No description available" + + prompt = f"""You are an expert in competitive programming education. Analyze the problem and solutions, then generate a detailed editorial in Vietnamese. + +## Problem Information +**Code**: {problem.code} +**Name**: {problem.name} +**Description**: {description} + +## Accepted Solutions +{formatted_solutions} + +## Instructions +Analyze the solutions above and create structured editorial data: +1. Explain the problem clearly in Vietnamese +2. Identify 2-3 different approaches from the solutions +3. Order from simplest to most optimal +4. Provide code, complexity, and explanation for each approach +5. List key insights and common pitfalls + +## Output Requirements +You MUST return valid JSON following this schema: +{{ + "problem_understanding": "string - clear explanation in Vietnamese", + "approaches": [ + {{ + "name": "string - approach name (e.g., 'Brute Force', 'Hash Map')", + "language": "string - programming language (lowercase, use 'cpp' for C++, 'csharp' for C#)", + "code": "string - code snippet", + "time_complexity": "string - e.g., O(n), O(n log n), can use ~10^9~ if needed", + "space_complexity": "string - e.g., O(1), O(n), can use ~10^9~ if needed", + "explanation": "string - detailed explanation in Vietnamese" + }} + ], + "key_insights": ["string - insight 1", "string - insight 2"], + "common_pitfalls": ["string - pitfall 1", "string - pitfall 2"] +}} + +DO NOT include any text outside the JSON object. +""" return prompt def call_openai_structured(self, prompt, attempt=0): @@ -262,14 +267,11 @@ def call_openai_structured(self, prompt, attempt=0): response = self.client.chat.completions.parse( model=self.model, messages=[ - { - 'role': 'system', - 'content': 'You are a competitive programming education expert. Always return valid JSON.', - }, - {'role': 'user', 'content': prompt}, + {"role": "system", "content": "You are a competitive programming education expert. Always return valid JSON."}, + {"role": "user", "content": prompt} ], temperature=self.temperature, - response_format=EditorialContent, + response_format=EditorialContent ) # Get the parsed result directly @@ -279,9 +281,8 @@ def call_openai_structured(self, prompt, attempt=0): except Exception as e: if attempt < self.max_retries: wait_time = self.retry_delay * (2 ** attempt) - err_msg = str(e) - self.logger.warning('API error (attempt %d/%d): %s', attempt + 1, self.max_retries, err_msg) - self.logger.info('Retrying in %d seconds...', wait_time) + self.logger.warning(f"API error (attempt {attempt + 1}/{self.max_retries}): {e}") + self.logger.info(f"Retrying in {wait_time} seconds...") time.sleep(wait_time) return self.call_openai_structured(prompt, attempt + 1) else: @@ -289,18 +290,18 @@ def call_openai_structured(self, prompt, attempt=0): def generate_editorial_content(self, problem, solutions): """Generate editorial content using OpenAI with structured output.""" - self.logger.info('Generating editorial for %s...', problem.code) + self.logger.info(f"Generating editorial for {problem.code}...") formatted_solutions = self.format_solutions_for_prompt(solutions) prompt = self.build_openai_prompt(problem, formatted_solutions) if self.verbose: - self.logger.debug('Prompt length: %d characters', len(prompt)) + self.logger.debug(f"Prompt length: {len(prompt)} characters") editorial = self.call_openai_structured(prompt) if self.verbose: - self.logger.debug('Generated structured editorial') + self.logger.debug(f"Generated structured editorial") return editorial @@ -308,53 +309,53 @@ def format_editorial_to_markdown(self, editorial: EditorialContent, problem): """Convert Pydantic EditorialContent to markdown format with Vietnamese headers.""" lines = [] # lines.append(f"# Editorial for {problem.code}: {problem.name}") - lines.append('') - lines.append('## Hiểu bài toán') + lines.append("") + lines.append("## Hiểu bài toán") lines.append(editorial.problem_understanding) - lines.append('') - lines.append('## Các cách tiếp cận') + lines.append("") + lines.append("## Các cách tiếp cận") for i, approach in enumerate(editorial.approaches, 1): # Normalize language code lang_code = approach.language.lower().replace('c++', 'cpp').replace('c#', 'csharp') - lines.append(f'### Cách {approach.name}') - lines.append('') - lines.append(f'```{lang_code}') + lines.append(f"### Cách {approach.name}") + lines.append("") + lines.append(f"```{lang_code}") lines.append(approach.code) - lines.append('```') - lines.append('') - lines.append(f'* **Time Complexity**: {approach.time_complexity}') - lines.append(f'* **Space Complexity**: {approach.space_complexity}') - lines.append('') + lines.append("```") + lines.append("") + lines.append(f"* **Time Complexity**: {approach.time_complexity}") + lines.append(f"* **Space Complexity**: {approach.space_complexity}") + lines.append("") lines.append(approach.explanation) - lines.append('') + lines.append("") - lines.append('## Phân tích độ phức tạp') - lines.append('| Cách tiếp cận | Time | Space | Tên |') - lines.append('|--------------|------|-------|-----|') + lines.append("## Phân tích độ phức tạp") + lines.append("| Cách tiếp cận | Time | Space | Tên |") + lines.append("|--------------|------|-------|-----|") for i, approach in enumerate(editorial.approaches, 1): - lines.append(f'| {i} | {approach.time_complexity} | {approach.space_complexity} | {approach.name} |') - lines.append('') + lines.append(f"| {i} | {approach.time_complexity} | {approach.space_complexity} | {approach.name} |") + lines.append("") - lines.append('## Bài học kinh nghiệm') + lines.append("## Bài học kinh nghiệm") for insight in editorial.key_insights: - lines.append(f'- {insight}') - lines.append('') + lines.append(f"- {insight}") + lines.append("") - lines.append('## Lỗi thường gặp') + lines.append("## Lỗi thường gặp") for pitfall in editorial.common_pitfalls: - lines.append(f'- {pitfall}') + lines.append(f"- {pitfall}") - return '\n'.join(lines) + return "\n".join(lines) def save_editorial(self, problem, editorial: EditorialContent, solutions, dry_run=False): """Save editorial to database.""" content = self.format_editorial_to_markdown(editorial, problem) if dry_run: - self.logger.info('[DRY RUN] Would create editorial for %s', problem.code) - self.logger.info('Content preview:\n%s...', content[:500]) + self.logger.info(f"[DRY RUN] Would create editorial for {problem.code}") + self.logger.info(f"Content preview:\n{content[:500]}...") return None # Get admin user as first author @@ -362,7 +363,7 @@ def save_editorial(self, problem, editorial: EditorialContent, solutions, dry_ru try: admin_user = User.objects.get(username='admin') admin_profile = admin_user.profile - except User.DoesNotExist: + except: # Fallback: get any superuser admin_user = User.objects.filter(is_superuser=True).first() if admin_user: @@ -391,7 +392,7 @@ def save_editorial(self, problem, editorial: EditorialContent, solutions, dry_ru problem=problem, content=content, is_public=True, - publish_on=timezone.now(), + publish_on=timezone.now() ) for author in authors: @@ -399,36 +400,36 @@ def save_editorial(self, problem, editorial: EditorialContent, solutions, dry_ru solution.save() - self.logger.info('✓ Created editorial for %s (ID: %s)', problem.code, solution.id) - self.logger.info(' - Authors: %s', ', '.join(a.user.username for a in authors)) - self.logger.info(' - Status: Public') - self.logger.info(' - Content: %d chars, %d approaches', len(content), len(editorial.approaches)) + self.logger.info(f"✓ Created editorial for {problem.code} (ID: {solution.id})") + self.logger.info(f" - Authors: {', '.join(a.user.username for a in authors)}") + self.logger.info(f" - Status: Public") + self.logger.info(f" - Content: {len(content)} chars, {len(editorial.approaches)} approaches") return solution - except Exception: - self.logger.exception('✗ Failed to save editorial for %s', problem.code) + except Exception as e: + self.logger.error(f"✗ Failed to save editorial for {problem.code}: {e}") raise def process_problem(self, problem): """Process a single problem.""" - self.logger.info('\n%s', '=' * 60) - self.logger.info('Processing: %s - %s', problem.code, problem.name) - self.logger.info('=' * 60) + self.logger.info(f"\n{'='*60}") + self.logger.info(f"Processing: {problem.code} - {problem.name}") + self.logger.info(f"{'='*60}") solutions = self.get_ac_solutions(problem, limit=3) - self.logger.info('Found %d AC solutions', len(solutions)) + self.logger.info(f"Found {len(solutions)} AC solutions") for i, sub in enumerate(solutions, 1): - lang = sub.language.common_name if sub.language else 'Unknown' - user = sub.user.user.username if sub.user and sub.user.user else 'Unknown' - self.logger.info(' %d. %s by %s (ID: %s)', i, lang, user, sub.id) + lang = sub.language.common_name if sub.language else "Unknown" + user = sub.user.user.username if sub.user and sub.user.user else "Unknown" + self.logger.info(f" {i}. {lang} by {user} (ID: {sub.id})") errors = self.validate_generation(problem, solutions) if errors: - self.logger.warning('✗ Validation failed for %s:', problem.code) + self.logger.warning(f"✗ Validation failed for {problem.code}:") for error in errors: - self.logger.warning(' - %s', error) + self.logger.warning(f" - {error}") return False try: @@ -436,16 +437,16 @@ def process_problem(self, problem): result = self.save_editorial(problem, editorial, solutions, self.dry_run) if self.dry_run: - self.logger.info('✓ Dry run successful: %s', problem.code) + self.logger.info(f"✓ Dry run successful: {problem.code}") return True elif result: - self.logger.info('✓ Success: %s', problem.code) + self.logger.info(f"✓ Success: {problem.code}") return True else: return False - except Exception: - self.logger.exception('✗ Failed to generate editorial for %s', problem.code) + except Exception as e: + self.logger.error(f"✗ Failed to generate editorial for {problem.code}: {e}") return False @@ -468,7 +469,7 @@ def handle(self, *args, **options): import os if not os.environ.get('OPENAI_API_KEY'): self.stdout.write(self.style.WARNING( - 'WARNING: OPENAI_API_KEY not set. Set with: export OPENAI_API_KEY="sk-..."\n', + "WARNING: OPENAI_API_KEY not set. Set with: export OPENAI_API_KEY='sk-...'\n" )) generator = EditorialGenerator(self, **options) @@ -477,30 +478,30 @@ def handle(self, *args, **options): problems = generator.get_problems_without_editorials( problem_code=options['problem'], limit=1, - offset=0, + offset=0 ) if not problems: self.stdout.write(self.style.ERROR( - f'Problem \'{options["problem"]}\' not found or already has editorial', + f"Problem '{options['problem']}' not found or already has editorial" )) return else: problems = generator.get_problems_without_editorials( limit=options['limit'], - offset=options['offset'], + offset=options['offset'] ) if not problems: - self.stdout.write(self.style.NOTICE('No problems found to process.')) + self.stdout.write(self.style.NOTICE("No problems found to process.")) return self.stdout.write( - self.style.SUCCESS(f'Found {len(problems)} problem(s) to process\n'), + self.style.SUCCESS(f"Found {len(problems)} problem(s) to process\n") ) if options['dry_run']: - self.stdout.write(self.style.WARNING('=== DRY RUN MODE ===')) - self.stdout.write('No changes will be saved to database\n') + self.stdout.write(self.style.WARNING("=== DRY RUN MODE ===")) + self.stdout.write("No changes will be saved to database\n") results = {'success': 0, 'failed': 0, 'skipped': 0} @@ -511,8 +512,23 @@ def handle(self, *args, **options): results['success'] += 1 else: results['failed'] += 1 - except Exception: - generator.logger.exception('Unexpected error processing %s', problem.code) + except Exception as e: + generator.logger.error(f"Unexpected error processing {problem.code}: {e}") results['failed'] += 1 - self.stdout.write('\n' + '=' * 60) + self.stdout.write("\n" + "="*60) + self.stdout.write(self.style.SUCCESS("SUMMARY")) + self.stdout.write("="*60) + self.stdout.write(f"Processed: {len(problems)}") + self.stdout.write(f"Success: {self.style.SUCCESS(str(results['success']))}") + self.stdout.write(f"Failed: {self.style.ERROR(str(results['failed']))}") + + if options['dry_run']: + self.stdout.write("\n" + self.style.WARNING( + "This was a dry run. No changes were saved.\n" + "Run without --dry-run to actually create editorials." + )) + else: + self.stdout.write("\n" + self.style.SUCCESS( + "Generated editorials are set to PUBLIC (is_public=True)." + )) diff --git a/judge/migrations/0215_populate_vote_counts.py b/judge/migrations/0215_populate_vote_counts.py index 7f0a9bfb1..b10908985 100644 --- a/judge/migrations/0215_populate_vote_counts.py +++ b/judge/migrations/0215_populate_vote_counts.py @@ -1,8 +1,7 @@ from django.db import migrations -from django.db.models import Count, IntegerField, OuterRef, Subquery +from django.db.models import Count, OuterRef, Subquery, IntegerField from django.db.models.functions import Coalesce - def populate_votes(apps, schema_editor): Comment = apps.get_model('judge', 'Comment') CommentVote = apps.get_model('judge', 'CommentVote') @@ -10,26 +9,25 @@ def populate_votes(apps, schema_editor): # Subquery to count upvotes (score = 1) upvotes_qs = CommentVote.objects.filter( comment=OuterRef('pk'), - score=1, + score=1 ).values('comment').annotate( - count=Count('id'), + count=Count('id') ).values('count') - + # Subquery to count downvotes (score = -1) downvotes_qs = CommentVote.objects.filter( comment=OuterRef('pk'), - score=-1, + score=-1 ).values('comment').annotate( - count=Count('id'), + count=Count('id') ).values('count') # Update all comments efficiently Comment.objects.update( upvotes=Coalesce(Subquery(upvotes_qs, output_field=IntegerField()), 0), - downvotes=Coalesce(Subquery(downvotes_qs, output_field=IntegerField()), 0), + downvotes=Coalesce(Subquery(downvotes_qs, output_field=IntegerField()), 0) ) - class Migration(migrations.Migration): dependencies = [ diff --git a/judge/views/comment.py b/judge/views/comment.py index d5383af1b..bc9f67925 100644 --- a/judge/views/comment.py +++ b/judge/views/comment.py @@ -77,7 +77,7 @@ def vote_comment(request, delta): # Update vote counts and ranking score comment = Comment.objects.get(id=comment_id) comment.vote(delta) - + # Update upvote/downvote counts if delta == 1: comment.upvotes = F('upvotes') + 1 diff --git a/judge/views/problem.py b/judge/views/problem.py index 7f87e87fe..30b4fa60f 100755 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -190,12 +190,6 @@ def get_comment_page(self): def get_context_data(self, **kwargs): context = super(ProblemDetail, self).get_context_data(**kwargs) - - # Detect mobile user agent - ua = self.request.META.get('HTTP_USER_AGENT', '').lower() - # 'mobi' covers iPhone, Android mobile, etc. iPad and Android tablets usually exclude 'mobi'. - context['is_mobile'] = 'mobi' in ua - user = self.request.user authed = user.is_authenticated contest_problem = self.contest_problem @@ -249,62 +243,6 @@ def get_context_data(self, **kwargs): context['description'], 'problem') context['meta_description'] = self.object.summary or metadata[0] context['og_image'] = self.object.og_image or metadata[1] - - # Add submit form context for unified view - # Set default language and theme - if authed: - default_lang = user.profile.language - ace_theme = user.profile.resolved_ace_theme - instance = Submission(user=user.profile, problem=self.object) - else: - # Guest user defaults - default_lang = None - ace_theme = 'twilight' - instance = None # No submission instance for guests - - # Create form - # We pass instance only if authed, or None which implies a new unsaved instance (unbound-ish) - form = ProblemSubmitForm( - instance=instance, - initial={'language': default_lang} if default_lang else {}, - ) - - # Set judge choices if user can edit problem - if authed and self.object.is_editable_by(user): - form.fields['judge'].choices = tuple( - Judge.objects.filter(online=True, problems=self.object).values_list('name', 'name'), - ) - - # Set language queryset - form.fields['language'].queryset = ( - self.object.usable_languages.order_by('name', 'key') - .prefetch_related(Prefetch('runtimeversion_set', RuntimeVersion.objects.order_by('priority'))) - ) - - # If default_lang is None (guest), try to pick the first one as default to avoid "unavailable" warning - if not default_lang and form.fields['language'].queryset.exists(): - default_lang = form.fields['language'].queryset.first() - form.initial['language'] = default_lang - - # Set ACE editor mode and theme - if default_lang: - form.fields['source'].widget.mode = default_lang.ace - form.fields['source'].widget.theme = ace_theme - - context['form'] = form - context['default_lang'] = default_lang - context['no_judges'] = not form.fields['language'].queryset.exists() - context['ACE_URL'] = settings.ACE_URL - - # Add submissions for unified view - if authed: - context['my_submissions'] = Submission.objects.filter( - user=user.profile, - problem=self.object, - ).order_by('-date')[:20] # Limit to 20 for initial view - else: - context['my_submissions'] = [] - return context diff --git a/locale/vi/LC_MESSAGES/django.po b/locale/vi/LC_MESSAGES/django.po index 1c9cb5fd3..155880e0d 100644 --- a/locale/vi/LC_MESSAGES/django.po +++ b/locale/vi/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: dmoj\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-29 01:35+0000\n" +"POT-Creation-Date: 2024-09-19 03:21+0000\n" "PO-Revision-Date: 2020-08-23 18:59\n" "Last-Translator: \n" "Language-Team: Vietnamese\n" @@ -17,27 +17,27 @@ msgstr "" "X-Crowdin-File: django.po\n" "X-Crowdin-File-ID: 10\n" -#: dmoj/settings.py:73 +#: dmoj/settings.py:70 msgid "Normal User" msgstr "" -#: dmoj/settings.py:74 +#: dmoj/settings.py:71 msgid "Problem Setter" msgstr "" -#: dmoj/settings.py:75 +#: dmoj/settings.py:72 msgid "Bedao Team" msgstr "" -#: dmoj/settings.py:76 +#: dmoj/settings.py:73 msgid "Staff" msgstr "" -#: dmoj/settings.py:77 +#: dmoj/settings.py:74 msgid "Banned User" msgstr "" -#: dmoj/settings.py:78 templates/base.html:302 templates/comments/list.html:102 +#: dmoj/settings.py:75 templates/base.html:282 templates/comments/list.html:102 #: templates/contest/contest-list-tabs.html:34 #: templates/contest/ranking-table.html:28 #: templates/problem/problem-list-tabs.html:15 @@ -48,47 +48,47 @@ msgstr "" msgid "Admin" msgstr "Quản trị" -#: dmoj/settings.py:79 +#: dmoj/settings.py:76 msgid "Teacher" msgstr "" -#: dmoj/settings.py:543 +#: dmoj/settings.py:533 msgid "English" msgstr "Tiếng Anh" -#: dmoj/settings.py:544 +#: dmoj/settings.py:534 msgid "Vietnamese" msgstr "Tiếng Việt" -#: dmoj/urls.py:40 +#: dmoj/urls.py:39 msgid "Activation Successful!" msgstr "Kích hoạt thành công" -#: dmoj/urls.py:48 +#: dmoj/urls.py:47 msgid "Registration Completed" msgstr "Đăng ký thành công" -#: dmoj/urls.py:52 +#: dmoj/urls.py:51 msgid "Registration Not Allowed" msgstr "Không được phép đăng ký" -#: dmoj/urls.py:59 +#: dmoj/urls.py:58 msgid "Password change successful" msgstr "Đổi mật khẩu thành công" -#: dmoj/urls.py:65 +#: dmoj/urls.py:64 msgid "Enter new password" msgstr "Nhập mật khẩu mới" -#: dmoj/urls.py:69 +#: dmoj/urls.py:68 msgid "Password reset complete" msgstr "Đặt lại mật khẩu thành công" -#: dmoj/urls.py:73 +#: dmoj/urls.py:72 msgid "Password reset sent" msgstr "Đã gửi email đặt lại mật khẩu" -#: dmoj/urls.py:105 templates/base.html:241 templates/organization/tabs.html:15 +#: dmoj/urls.py:104 templates/base.html:221 templates/organization/tabs.html:15 msgid "Home" msgstr "Trang chủ" @@ -117,139 +117,139 @@ msgid_plural "%d comments successfully unhidden." msgstr[0] "%d bình luận đã được hiện lại." #: judge/admin/comments.py:56 judge/models/comment.py:31 -#: judge/models/comment.py:210 +#: judge/models/comment.py:208 msgid "associated page" msgstr "trang liên kết" -#: judge/admin/contest.py:32 +#: judge/admin/contest.py:33 msgid "Included contests" msgstr "Các kỳ thi" -#: judge/admin/contest.py:66 judge/forms.py:687 +#: judge/admin/contest.py:67 judge/forms.py:644 #: templates/contest/contest.html:200 templates/contest/contest.html:310 -#: templates/contest/moss.html:40 templates/problem/list.html:236 +#: templates/contest/moss.html:40 templates/problem/list.html:162 #: templates/user/user-problems.html:58 templates/user/user-problems.html:102 msgid "Problem" msgstr "Bài" -#: judge/admin/contest.py:67 templates/contest/contest.html:189 -#: templates/contest/edit.html:109 templates/contest/edit.html:116 +#: judge/admin/contest.py:68 templates/contest/contest.html:189 +#: templates/contest/edit.html:108 templates/contest/edit.html:115 msgid "Problems" msgstr "Danh sách bài" -#: judge/admin/contest.py:78 judge/admin/submission.py:249 +#: judge/admin/contest.py:79 judge/admin/submission.py:249 #: templates/admin/judge/submission/change_form.html:14 #: templates/admin/judge/submission/change_form.html:17 #: templates/submission/source.html:76 templates/submission/status.html:93 msgid "Rejudge" msgstr "Chấm lại" -#: judge/admin/contest.py:100 +#: judge/admin/contest.py:101 #, fuzzy #| msgid "push announcements" msgid "Resend announcement" msgstr "Gửi thông báo đến thí sinh" -#: judge/admin/contest.py:143 +#: judge/admin/contest.py:147 msgid "Settings" msgstr "Cài đặt" -#: judge/admin/contest.py:148 +#: judge/admin/contest.py:152 msgid "Scheduling" msgstr "Lịch" -#: judge/admin/contest.py:150 +#: judge/admin/contest.py:154 msgid "Details" msgstr "Chi tiết" -#: judge/admin/contest.py:151 +#: judge/admin/contest.py:155 msgid "Format" msgstr "" -#: judge/admin/contest.py:152 templates/contest/ranking-table.html:72 +#: judge/admin/contest.py:156 templates/contest/ranking-table.html:72 msgid "Rating" msgstr "" -#: judge/admin/contest.py:154 +#: judge/admin/contest.py:157 msgid "Access" msgstr "Truy cập" -#: judge/admin/contest.py:156 judge/admin/problem.py:136 +#: judge/admin/contest.py:159 judge/admin/problem.py:138 msgid "Justice" msgstr "Công lý" -#: judge/admin/contest.py:157 +#: judge/admin/contest.py:160 msgid "Ranking" msgstr "Bảng xếp hạng" -#: judge/admin/contest.py:244 +#: judge/admin/contest.py:247 msgid "Mark contests as visible" msgstr "Đánh dấu các kỳ thi là có thể thấy" -#: judge/admin/contest.py:249 +#: judge/admin/contest.py:252 #, 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 là có thể thấy." -#: judge/admin/contest.py:253 +#: judge/admin/contest.py:256 msgid "Mark contests as hidden" msgstr "Đánh dấu các kỳ thi là ẩn" -#: judge/admin/contest.py:258 +#: judge/admin/contest.py:261 #, 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 là ẩn." -#: judge/admin/contest.py:262 +#: judge/admin/contest.py:265 msgid "Lock contest submissions" msgstr "" -#: judge/admin/contest.py:267 +#: judge/admin/contest.py:270 #, python-format msgid "%d contest successfully locked." msgid_plural "%d contests successfully locked." msgstr[0] "" -#: judge/admin/contest.py:271 +#: judge/admin/contest.py:274 msgid "Unlock contest submissions" msgstr "" -#: judge/admin/contest.py:276 +#: judge/admin/contest.py:279 #, python-format msgid "%d contest successfully unlocked." msgid_plural "%d contests successfully unlocked." msgstr[0] "" -#: judge/admin/contest.py:306 judge/admin/submission.py:181 +#: judge/admin/contest.py:309 judge/admin/submission.py:181 #, 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 để chấm lại." -#: judge/admin/contest.py:321 judge/admin/submission.py:210 +#: judge/admin/contest.py:324 judge/admin/submission.py:210 #: judge/views/problem_manage.py:131 #, python-format msgid "%d submission was successfully rescored." msgid_plural "%d submissions were successfully rescored." msgstr[0] "%d bài đã được tính điểm lại." -#: judge/admin/contest.py:404 +#: judge/admin/contest.py:407 msgid "Recalculate results" msgstr "Tính lại kết quả" -#: judge/admin/contest.py:410 +#: judge/admin/contest.py:413 #, python-format msgid "%d participation recalculated." msgid_plural "%d participations recalculated." msgstr[0] "%d lượt tham gia đã được tính lại." -#: judge/admin/contest.py:414 judge/admin/organization.py:71 +#: judge/admin/contest.py:417 judge/admin/organization.py:71 msgid "username" msgstr "tên người dùng" -#: judge/admin/contest.py:418 templates/base.html:343 +#: judge/admin/contest.py:421 templates/base.html:321 msgid "virtual" msgstr "ảo" @@ -261,15 +261,15 @@ msgstr "đường dẫn liên kết" msgid "Summary" msgstr "Tóm tắt" -#: judge/admin/interface.py:92 judge/admin/problem.py:174 -#: judge/models/interface.py:69 judge/models/problem.py:673 +#: judge/admin/interface.py:92 judge/admin/problem.py:176 +#: judge/models/interface.py:69 judge/models/problem.py:672 msgid "authors" msgstr "tác giả" -#: judge/admin/interface.py:122 judge/admin/profile.py:111 -#: judge/admin/submission.py:222 judge/models/contest.py:571 -#: judge/models/contest.py:735 judge/models/profile.py:468 -#: judge/models/profile.py:499 judge/models/ticket.py:39 +#: judge/admin/interface.py:122 judge/admin/profile.py:112 +#: judge/admin/submission.py:222 judge/models/contest.py:566 +#: judge/models/contest.py:730 judge/models/profile.py:460 +#: judge/models/profile.py:491 judge/models/ticket.py:39 msgid "user" msgstr "thành viên" @@ -277,12 +277,12 @@ msgstr "thành viên" msgid "object" msgstr "đối tượng" -#: judge/admin/organization.py:34 judge/admin/problem.py:180 -#: judge/admin/profile.py:109 +#: judge/admin/organization.py:34 judge/admin/problem.py:182 +#: judge/admin/profile.py:110 msgid "View on site" msgstr "Xem trên trang web" -#: judge/admin/organization.py:56 judge/admin/profile.py:127 +#: judge/admin/organization.py:56 judge/admin/profile.py:128 msgid "Recalculate scores" msgstr "Tính lại điểm" @@ -296,102 +296,102 @@ msgstr[0] "Đã tính lại điểm của %d tổ chức." msgid "Describe the changes you made (optional)" msgstr "Mô tả những thay đổi bạn đã thực hiện (tùy chọn)" -#: judge/admin/problem.py:131 +#: judge/admin/problem.py:133 msgid "Social Media" msgstr "Mạng Xã hội" -#: judge/admin/problem.py:132 +#: judge/admin/problem.py:134 msgid "Taxonomy" msgstr "Phân loại" -#: judge/admin/problem.py:133 templates/blog/top-pp.html:9 -#: templates/contest/contest.html:202 templates/contest/edit.html:117 +#: judge/admin/problem.py:135 templates/blog/top-pp.html:9 +#: templates/contest/contest.html:202 templates/contest/edit.html:116 #: templates/contest/official-ranking-table.html:12 -#: templates/organization/list.html:24 templates/problem/data.html:727 -#: templates/problem/list.html:247 templates/problem/unified_view.html:31 -#: templates/user/base-users-table.html:12 templates/user/user-problems.html:60 +#: templates/organization/list.html:24 templates/problem/data.html:724 +#: templates/problem/list.html:173 templates/user/base-users-table.html:12 +#: templates/user/user-problems.html:60 msgid "Points" msgstr "Điểm" -#: judge/admin/problem.py:134 +#: judge/admin/problem.py:136 msgid "Limits" msgstr "Giới hạn" -#: judge/admin/problem.py:135 templates/problem/editor.html:119 +#: judge/admin/problem.py:137 templates/problem/editor.html:118 #: templates/problem/submission-diff.html:114 -#: templates/submission/list.html:391 +#: templates/submission/list.html:327 msgid "Language" msgstr "Ngôn ngữ" -#: judge/admin/problem.py:137 +#: judge/admin/problem.py:139 msgid "History" msgstr "Lịch sử" -#: judge/admin/problem.py:186 +#: judge/admin/problem.py:188 msgid "Mark problems as public and set publish date to now" msgstr "Công bố bài và đặt ngày công bố là bây giờ" -#: judge/admin/problem.py:192 +#: judge/admin/problem.py:194 #, 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 công bố." -#: judge/admin/problem.py:196 +#: judge/admin/problem.py:198 msgid "Mark problems as private" msgstr "Ẩn bài" -#: judge/admin/problem.py:201 +#: judge/admin/problem.py:203 #, python-format msgid "%d problem successfully marked as private." msgid_plural "%d problems successfully marked as private." msgstr[0] "Đã ẩn %d bài." -#: judge/admin/profile.py:39 judge/admin/profile.py:119 +#: judge/admin/profile.py:40 judge/admin/profile.py:120 msgid "timezone" msgstr "múi giờ" -#: judge/admin/profile.py:115 +#: judge/admin/profile.py:116 #, fuzzy #| msgid "Email" msgid "email" msgstr "Địa chỉ email" -#: judge/admin/profile.py:123 +#: judge/admin/profile.py:124 msgid "date joined" msgstr "ngày tham gia" -#: judge/admin/profile.py:133 +#: judge/admin/profile.py:134 #, python-format msgid "%d user had scores recalculated." msgid_plural "%d users had scores recalculated." msgstr[0] "Đã tính lại điểm của %d người dùng." -#: judge/admin/profile.py:137 +#: judge/admin/profile.py:138 msgid "Recalulate contribution points" msgstr "Tính lại điểm đóng góp" -#: judge/admin/profile.py:143 +#: judge/admin/profile.py:144 #, python-format msgid "%d user has contribution scores recalculated." msgid_plural "%d users have contribution scores recalculated." msgstr[0] "Đã tính lại điểm đóng góp của %d người dùng." -#: judge/admin/runtime.py:62 templates/user/edit-profile.html:120 +#: judge/admin/runtime.py:63 templates/user/edit-profile.html:120 #: templates/user/edit-profile.html:251 templates/user/edit-profile.html:400 msgid "Regenerate" msgstr "Tạo lại" -#: judge/admin/runtime.py:76 templates/contest/contest.html:277 -#: templates/contest/contest.html:311 templates/problem/unified_view.html:5 +#: judge/admin/runtime.py:77 templates/contest/contest.html:277 +#: templates/contest/contest.html:311 msgid "Description" msgstr "Mô tả" -#: judge/admin/runtime.py:77 +#: judge/admin/runtime.py:78 msgid "Information" msgstr "Thông tin" -#: judge/admin/runtime.py:78 +#: judge/admin/runtime.py:79 msgid "Capabilities" msgstr "Khả năng" @@ -454,7 +454,7 @@ msgstr "mã bài" msgid "problem name" msgstr "tên bài toán" -#: judge/admin/submission.py:226 judge/models/profile.py:156 +#: judge/admin/submission.py:226 judge/models/profile.py:148 #, fuzzy #| msgid "{time}" msgid "time" @@ -476,8 +476,8 @@ msgstr "%d KB" msgid "%.2f MB" msgstr "%.2f MB" -#: judge/admin/submission.py:240 judge/models/problem.py:635 -#: judge/models/problem.py:654 judge/models/runtime.py:120 +#: judge/admin/submission.py:240 judge/models/problem.py:634 +#: judge/models/problem.py:653 judge/models/runtime.py:120 msgid "language" msgstr "ngôn ngữ" @@ -701,7 +701,7 @@ msgstr "Mật khẩu này chỉ toàn các số." msgid "Your password can't be entirely numeric." msgstr "Mật khẩu không được hoàn toàn bằng số." -#: judge/custom_translations.py:24 templates/base.html:266 +#: judge/custom_translations.py:24 templates/base.html:246 msgid "PRoblems" msgstr "Danh sách bài" @@ -710,7 +710,7 @@ msgstr "Danh sách bài" msgid "posted {time}" msgstr "đã đăng {time}" -#: judge/custom_translations.py:28 templates/comments/list.html:67 +#: judge/custom_translations.py:28 templates/comments/list.html:65 #: templates/user/comment.html:71 #, python-brace-format msgid "commented {time}" @@ -729,94 +729,94 @@ msgid "%(time_limit)s window" msgstr "" "Thời gian làm bài: %(time_limit)s (bắt đầu tính giờ khi nhấn tham gia kỳ thi)" -#: judge/forms.py:35 +#: judge/forms.py:36 #, python-brace-format msgid "Two-factor authentication tokens must be {count} decimal digit." msgid_plural "Two-factor authentication tokens must be {count} decimal digits." msgstr[0] "" -#: judge/forms.py:40 +#: judge/forms.py:41 msgid "Invalid two-factor authentication token." msgstr "" -#: judge/forms.py:43 +#: judge/forms.py:44 msgid "Scratch codes must be 16 Base32 characters." msgstr "" -#: judge/forms.py:45 judge/forms.py:644 +#: judge/forms.py:46 judge/forms.py:601 msgid "Invalid scratch code." msgstr "" -#: judge/forms.py:52 +#: judge/forms.py:53 msgid "Subscribe to contest updates" msgstr "Đăng ký để nhận các cập nhật về kỳ thi" -#: judge/forms.py:53 +#: judge/forms.py:54 msgid "Enable experimental features" msgstr "Bật các tính năng đang thử nghiệm" -#: judge/forms.py:81 +#: judge/forms.py:82 #, python-format msgid "You must solve at least %d problems before you can update your profile." msgstr "" "Bạn phải giải ít nhất %d bài trước khi có thể thay đổi thông tin người dùng" -#: judge/forms.py:90 judge/views/organization.py:220 judge/views/register.py:61 +#: judge/forms.py:91 judge/views/organization.py:204 judge/views/register.py:61 #, python-brace-format msgid "You may not be part of more than {count} public organization." msgid_plural "You may not be part of more than {count} public organizations." msgstr[0] "" "Bạn không thể là thành viên của nhiều hơn {count} tổ chức công khai." -#: judge/forms.py:125 +#: judge/forms.py:126 msgid "Your full name is too long!" msgstr "" -#: judge/forms.py:149 judge/forms.py:219 +#: judge/forms.py:150 judge/forms.py:220 #, python-format msgid "You cannot set time limit higher than %d seconds" msgstr "Bạn không được giới hạn thời gian lớn hơn %d giây" -#: judge/forms.py:166 +#: judge/forms.py:167 #, python-format msgid "Maximum file size is %s." msgstr "Độ lớn tối đa của file là %s." -#: judge/forms.py:168 +#: judge/forms.py:169 msgid "Statement file" msgstr "File đề bài" -#: judge/forms.py:181 +#: judge/forms.py:182 msgid "Private users" msgstr "Các thành viên riêng tư" -#: judge/forms.py:182 +#: judge/forms.py:183 msgid "If private, only these users may see the problem." msgstr "" "Nếu bài tập này không công khai, chỉ những thành viên này có thể thấy được " "bài tập." -#: judge/forms.py:189 judge/forms.py:764 +#: judge/forms.py:190 judge/forms.py:721 msgid "You can paste a list of usernames into this box." msgstr "" "Có thể gán một danh sách các username vào đây thay vì gõ tay (phân cách bằng " "khoảng trắng hoặc dấu phẩy)." -#: judge/forms.py:198 +#: judge/forms.py:199 #, python-format msgid "Problem id code must starts with `%s`" msgstr "Mã bài tập phải bắt đầu bằng `%s`" -#: judge/forms.py:206 judge/forms.py:400 +#: judge/forms.py:207 judge/forms.py:391 #, python-format msgid "File size is too big! Maximum file size is %s" msgstr "File quá lớn! Độ lớn tối đa của file là %s." -#: judge/forms.py:210 +#: judge/forms.py:211 msgid "You don't have permission to upload file-type statement." msgstr "Bạn không có quyền đăng file đề" -#: judge/forms.py:242 +#: judge/forms.py:243 msgid "" "If public, all members in organization can view it. Set it as " "private if you want to use it in a contest, otherwise, users can see the " @@ -827,11 +827,11 @@ msgstr "" "không, các thành viên có thể xem bài tập này kể cả khi họ không tham gia kỳ " "thi!" -#: judge/forms.py:245 +#: judge/forms.py:246 msgid "Problem code, e.g: voi19_post" msgstr "Mã bài, ví dụ: voi19_post" -#: judge/forms.py:246 +#: judge/forms.py:247 msgid "" "The full name of the problem, as shown in the problem list. For example: " "VOI19 - A cong B" @@ -839,7 +839,7 @@ msgstr "" "Tên đầy đủ của bài, được hiển thị trong danh sách bài. Ví dụ: VOI19 - A cộng " "B" -#: judge/forms.py:248 +#: judge/forms.py:249 msgid "" "Points awarded for problem completion. From 0 to 2. You can approximate: 0.5 " "is as hard as Problem 1 of VOI; 1 = Problem 2 of VOI; 1.5 = Problem 3 of VOI." @@ -847,65 +847,65 @@ msgstr "" "Điểm của bài, từ 0 tới 2. Có thể xấp xỉ điểm như sau: bài có điểm 0.5 sẽ khó " "như bài 1 thi HSG QG; 1 điểm = bài 2; 1.5 điểm = bài 3." -#: judge/forms.py:254 judge/forms.py:824 +#: judge/forms.py:255 judge/forms.py:780 msgid "Only accept alphanumeric characters (a-z, 0-9) and underscore (_)" msgstr "" "Chỉ có thể chứa các ký tự viết thường (a-z), số (0-9) và dấu gạch dưới (_)" -#: judge/forms.py:270 judge/forms.py:650 judge/models/problem.py:141 +#: judge/forms.py:261 judge/forms.py:607 judge/models/problem.py:141 msgid "Problem code must be ^[a-z0-9_]+$" msgstr "Mã bài phải khớp regex ^[a-z0-9_]+$" -#: judge/forms.py:272 +#: judge/forms.py:263 msgid "Package" msgstr "" -#: judge/forms.py:275 +#: judge/forms.py:266 msgid "Ignore zero-point batches" msgstr "" -#: judge/forms.py:276 +#: judge/forms.py:267 msgid "Ignore zero-point cases" msgstr "" -#: judge/forms.py:278 +#: judge/forms.py:269 msgid "Append main solution to tutorial" msgstr "" -#: judge/forms.py:308 +#: judge/forms.py:299 msgid "Download comments?" msgstr "Tải bình luận?" -#: judge/forms.py:309 judge/forms.py:338 +#: judge/forms.py:300 judge/forms.py:329 msgid "Download submissions?" msgstr "Tải bài nộp?" -#: judge/forms.py:310 judge/forms.py:339 +#: judge/forms.py:301 judge/forms.py:330 msgid "Filter by problem code glob:" msgstr "Lọc theo mã bài:" -#: judge/forms.py:314 judge/forms.py:343 +#: judge/forms.py:305 judge/forms.py:334 msgid "Leave empty to include all submissions" msgstr "Bỏ trống để tải tất cả bài nộp" -#: judge/forms.py:317 judge/forms.py:346 +#: judge/forms.py:308 judge/forms.py:337 #: templates/problem/manage_submission.html:158 msgid "Filter by result:" msgstr "Lọc theo kết quả:" -#: judge/forms.py:323 judge/forms.py:353 +#: judge/forms.py:314 judge/forms.py:344 msgid "Please select at least one thing to download." msgstr "Vui lòng chọn ít nhất một mục để tải." -#: judge/forms.py:370 +#: judge/forms.py:361 msgid "Source file" msgstr "File nộp" -#: judge/forms.py:388 +#: judge/forms.py:379 msgid "Source code/file is missing or redundant. Please try again" msgstr "Code/file code đang thiếu hoặc bị thừa. Xin hãy thử lại" -#: judge/forms.py:395 +#: judge/forms.py:386 #, python-format msgid "" "Wrong file type for language %(lang)s, expected %(lang_ext)s, found %(ext)s" @@ -913,102 +913,101 @@ msgstr "" "Sai loại file cho ngôn ngữ %(lang)s, chỉ chấp nhận: %(lang_ext)s, file nộp: " "%(ext)s" -#: judge/forms.py:408 +#: judge/forms.py:399 #, fuzzy, python-format #| msgid "File size is too big! Maximum file size is %s" msgid "project.json is too big! Maximum file size is %s" msgstr "File quá lớn! Độ lớn tối đa của file là %s." -#: judge/forms.py:423 +#: judge/forms.py:414 msgid "Any judge" msgstr "" -#: judge/forms.py:434 judge/models/tag.py:35 +#: judge/forms.py:425 judge/models/tag.py:35 msgid "Problem URL" msgstr "Link bài" -#: judge/forms.py:435 +#: judge/forms.py:426 msgid "Full URL to the problem, e.g. https://oj.vnoi.info/problem/post" msgstr "Link tới bài tập, ví dụ: https://oj.vnoi.info/problem/post" -#: judge/forms.py:525 judge/views/register.py:27 +#: judge/forms.py:482 judge/views/register.py:27 #: templates/blog/top-contrib.html:8 templates/blog/top-pp.html:8 #: templates/contest/official-ranking-table.html:5 #: templates/contest/ranking.html:414 #: templates/problem/submission-diff.html:112 -#: templates/registration/login.html:151 -#: templates/registration/registration_form.html:269 +#: templates/registration/registration_form.html:169 #: templates/user/base-users-table.html:7 msgid "Username" msgstr "Tên truy cập" -#: judge/forms.py:526 templates/registration/login.html:155 -#: templates/registration/registration_form.html:293 +#: judge/forms.py:483 templates/registration/registration_form.html:181 +#: templates/registration/registration_form.html:195 msgid "Password" msgstr "Mật khẩu" -#: judge/forms.py:541 +#: judge/forms.py:498 #, python-format msgid "This account has been banned. Reason: %s" msgstr "Tài khoản này đã bị cấm vì lý do: %s" -#: judge/forms.py:572 +#: judge/forms.py:529 msgid "Invalid code length." msgstr "" -#: judge/forms.py:603 +#: judge/forms.py:560 msgid "Invalid WebAuthn response." msgstr "" -#: judge/forms.py:606 +#: judge/forms.py:563 msgid "No WebAuthn challenge issued." msgstr "" -#: judge/forms.py:612 +#: judge/forms.py:569 msgid "Invalid WebAuthn credential ID." msgstr "" -#: judge/forms.py:642 +#: judge/forms.py:599 msgid "Invalid two-factor authentication token or scratch code." msgstr "" -#: judge/forms.py:646 +#: judge/forms.py:603 msgid "Must specify either totp_token or webauthn_response." msgstr "" -#: judge/forms.py:655 +#: judge/forms.py:612 msgid "Problem with code already exists." msgstr "Mã bài tập đã tồn tại." -#: judge/forms.py:669 judge/models/contest.py:75 +#: judge/forms.py:626 judge/models/contest.py:75 msgid "Contest id must be ^[a-z0-9_]+$" msgstr "id kỳ thi phải khớp regex ^[a-z0-9_]+$" -#: judge/forms.py:674 +#: judge/forms.py:631 msgid "Contest with key already exists." msgstr "Mã kỳ thi đã tồn tại." -#: judge/forms.py:698 +#: judge/forms.py:655 #, fuzzy #| msgid "No such problem" msgid "No such problem." msgstr "Không có bài" -#: judge/forms.py:722 +#: judge/forms.py:679 msgid "Problems must have distinct order." msgstr "Các bài tập phải có thứ tự khác nhau." -#: judge/forms.py:774 +#: judge/forms.py:731 #, python-format msgid "Contest duration cannot be longer than %d days" msgstr "Thời gian diễn ra contest không được kéo dài quá %d ngày" -#: judge/forms.py:786 +#: judge/forms.py:743 #, python-format msgid "Contest id must starts with `%s`" msgstr "Mã kỳ thi phải bắt đầu bằng `%s`" -#: judge/forms.py:819 +#: judge/forms.py:775 msgid "" "Users are able to pratice contest problems even if the contest has ended, so " "don't set the contest time too high if you don't really need it." @@ -1021,66 +1020,6 @@ msgstr "" msgid "N j, Y, g:i a" msgstr "j, M, Y, G:i" -#: judge/jinja2/filesize.py:33 -#, python-format -msgid "%s B" -msgstr "" - -#: judge/jinja2/filesize.py:33 -#, python-format -msgid "%s KB" -msgstr "" - -#: judge/jinja2/filesize.py:33 -#, python-format -msgid "%s MB" -msgstr "" - -#: judge/jinja2/filesize.py:33 -#, python-format -msgid "%s GB" -msgstr "" - -#: judge/jinja2/filesize.py:33 -#, python-format -msgid "%s TB" -msgstr "" - -#: judge/jinja2/filesize.py:33 -#, python-format -msgid "%s PB" -msgstr "" - -#: judge/jinja2/filesize.py:40 -#, python-format -msgid "%sB" -msgstr "" - -#: judge/jinja2/filesize.py:40 -#, python-format -msgid "%sK" -msgstr "" - -#: judge/jinja2/filesize.py:40 -#, python-format -msgid "%sM" -msgstr "" - -#: judge/jinja2/filesize.py:40 -#, python-format -msgid "%sG" -msgstr "" - -#: judge/jinja2/filesize.py:40 -#, python-format -msgid "%sT" -msgstr "" - -#: judge/jinja2/filesize.py:40 -#, python-format -msgid "%sP" -msgstr "" - #: judge/models/choices.py:24 msgid "Follow site theme" msgstr "" @@ -1151,50 +1090,38 @@ msgstr "cấp trên" msgid "revisions" msgstr "bài nộp" -#: judge/models/comment.py:39 -#, fuzzy -#| msgid "votes" -msgid "upvotes" -msgstr "đánh giá" - -#: judge/models/comment.py:40 -#, fuzzy -#| msgid "votes" -msgid "downvotes" -msgstr "đánh giá" - -#: judge/models/comment.py:44 +#: judge/models/comment.py:42 msgid "View all comments by a user" msgstr "" -#: judge/models/comment.py:46 +#: judge/models/comment.py:44 msgid "comment" msgstr "bình luận" -#: judge/models/comment.py:47 +#: judge/models/comment.py:45 msgid "comments" msgstr "nhận xét" -#: judge/models/comment.py:94 judge/models/comment.py:155 -#: judge/models/problem.py:684 +#: judge/models/comment.py:92 judge/models/comment.py:153 +#: judge/models/problem.py:683 #, python-format msgid "Editorial for %s" msgstr "Hướng giải cho %s" -#: judge/models/comment.py:183 +#: judge/models/comment.py:181 #, python-format msgid "%(page)s by %(user)s" msgstr "%(page)s bởi %(user)s" -#: judge/models/comment.py:205 +#: judge/models/comment.py:203 msgid "comment vote" msgstr "đánh giá bình luận" -#: judge/models/comment.py:206 +#: judge/models/comment.py:204 msgid "comment votes" msgstr "đánh giá bình luận" -#: judge/models/comment.py:215 +#: judge/models/comment.py:213 msgid "Override comment lock" msgstr "Ghi đè khóa nhận xét" @@ -1222,7 +1149,7 @@ msgstr "mô tả thẻ" msgid "contest tag" msgstr "thẻ kỳ thi" -#: judge/models/contest.py:60 judge/models/contest.py:160 +#: judge/models/contest.py:60 judge/models/contest.py:155 msgid "contest tags" msgstr "thẻ kỳ thi" @@ -1266,16 +1193,16 @@ msgstr "" msgid "These users will be able to view the contest, but not edit it." msgstr "Những người này có thể xem kỳ thi nhưng không thể chỉnh sửa chúng." -#: judge/models/contest.py:85 judge/models/runtime.py:151 +#: judge/models/contest.py:85 judge/models/runtime.py:148 msgid "description" msgstr "mô tả" -#: judge/models/contest.py:86 judge/models/problem.py:630 -#: judge/models/runtime.py:153 +#: judge/models/contest.py:86 judge/models/problem.py:629 +#: judge/models/runtime.py:150 msgid "problems" msgstr "danh sách bài" -#: judge/models/contest.py:87 judge/models/contest.py:572 +#: judge/models/contest.py:87 judge/models/contest.py:567 msgid "start time" msgstr "thời gian bắt đầu" @@ -1296,7 +1223,7 @@ msgid "registration end time" msgstr "thời gian tạo" #: judge/models/contest.py:93 judge/models/problem.py:167 -#: judge/models/problem.py:655 +#: judge/models/problem.py:654 msgid "time limit" msgstr "giới hạn thời gian" @@ -1391,126 +1318,115 @@ msgstr "Thí sinh có rating cao hơn sẽ không được xếp hạng." msgid "rate all" msgstr "Rate tất cả" -#: judge/models/contest.py:126 -#, fuzzy -#| msgid "Rescore the selected submissions" -msgid "Rate users even if they make no submissions." -msgstr "Tính điểm lại các bài đã chọn" +#: judge/models/contest.py:125 +msgid "Rate all users who joined." +msgstr "" +"Tính rating tất cả những người đã tham gia kỳ thi (kể cả không nộp bài nào)." -#: judge/models/contest.py:128 +#: judge/models/contest.py:126 msgid "exclude from ratings" msgstr "những người không xếp hạng" -#: judge/models/contest.py:130 -#, fuzzy -#| msgid "is disqualified" -msgid "rate disqualified" -msgstr "bị loại" - -#: judge/models/contest.py:131 -msgid "Rate users even if they are disqualified." -msgstr "" - -#: judge/models/contest.py:133 +#: judge/models/contest.py:128 msgid "private to specific users" msgstr "Kỳ thi riêng tư cho một số thành viên" -#: judge/models/contest.py:134 +#: judge/models/contest.py:129 msgid "private contestants" msgstr "Các thành viên có thể tham gia kỳ thi" -#: judge/models/contest.py:135 +#: judge/models/contest.py:130 msgid "If private, only these users may see the contest." msgstr "Nếu kỳ thi riêng tư, chỉ người thành viên này có thể tham gia kỳ thi." -#: judge/models/contest.py:137 +#: judge/models/contest.py:132 msgid "hide problem tags" msgstr "ẩn các thẻ đầu bài" -#: judge/models/contest.py:138 +#: judge/models/contest.py:133 msgid "Whether problem tags should be hidden by default." msgstr "Ẩn tags của bài tập" -#: judge/models/contest.py:140 +#: judge/models/contest.py:135 msgid "hide problem authors" msgstr "ẩn tác giả" -#: judge/models/contest.py:141 +#: judge/models/contest.py:136 msgid "Whether problem authors should be hidden by default." msgstr "Ẩn tác giả của bài tập" -#: judge/models/contest.py:143 +#: judge/models/contest.py:138 msgid "run pretests only" msgstr "chỉ chấm pretests" -#: judge/models/contest.py:144 +#: judge/models/contest.py:139 msgid "" "Whether judges should grade pretests only, versus all testcases. Commonly " "set during a contest, then unset prior to rejudging user submissions when " "the contest ends." msgstr "Trong khi kỳ thi diễn ra, chỉ chấm pretests." -#: judge/models/contest.py:148 +#: judge/models/contest.py:143 msgid "show short form settings display" msgstr "Hiển thị các cài đặt của kỳ thi" -#: judge/models/contest.py:149 +#: judge/models/contest.py:144 msgid "" "Whether to show a section containing contest settings on the contest page or " "not." msgstr "Hiện tóm tắt các cài đặt của kỳ thi ở trang kỳ thi." -#: judge/models/contest.py:152 judge/models/problem.py:219 +#: judge/models/contest.py:147 judge/models/problem.py:219 msgid "private to organizations" msgstr "dành riêng cho tổ chức" -#: judge/models/contest.py:153 judge/models/problem.py:217 -#: judge/models/profile.py:150 +#: judge/models/contest.py:148 judge/models/problem.py:217 +#: judge/models/profile.py:142 msgid "organizations" msgstr "tổ chức" -#: judge/models/contest.py:154 +#: judge/models/contest.py:149 msgid "If private, only these organizations may see the contest" msgstr "Nếu là private, thì chỉ các tổ chức này mới có thể xem kỳ thi" -#: judge/models/contest.py:155 judge/models/interface.py:76 +#: judge/models/contest.py:150 judge/models/interface.py:76 #: judge/models/problem.py:195 msgid "OpenGraph image" msgstr "Ảnh OpenGraph" -#: judge/models/contest.py:156 judge/models/profile.py:62 +#: judge/models/contest.py:151 judge/models/profile.py:62 msgid "logo override image" msgstr "Logo" -#: judge/models/contest.py:158 +#: judge/models/contest.py:153 msgid "" "This image will replace the default site logo for users inside the contest." msgstr "" "Link tới logo của kỳ thi. Ví dụ: https://oj.vnoi.info/martor/logo/hoadao.png" -#: judge/models/contest.py:161 +#: judge/models/contest.py:156 msgid "the amount of live participants" msgstr "số lượng người tham gia kỳ thi" -#: judge/models/contest.py:162 +#: judge/models/contest.py:157 msgid "the amount of virtual participants" msgstr "số lượng tham gia ảo" -#: judge/models/contest.py:163 +#: judge/models/contest.py:158 msgid "contest summary" msgstr "tổng kết kỳ thi" -#: judge/models/contest.py:164 judge/models/problem.py:197 +#: judge/models/contest.py:159 judge/models/problem.py:197 msgid "Plain-text, shown in meta description tag, e.g. for social media." msgstr "" "Các thông tin này sẽ hiển thị khi chia sẻ kỳ thi lên mạng xã hội (ví dụ " "discord, facebook)." -#: judge/models/contest.py:165 judge/models/profile.py:61 +#: judge/models/contest.py:160 judge/models/profile.py:61 msgid "access code" msgstr "mã truy cập" -#: judge/models/contest.py:166 +#: judge/models/contest.py:161 msgid "" "An optional code to prompt contestants before they are allowed to join the " "contest. Leave it blank to disable." @@ -1518,35 +1434,35 @@ msgstr "" "Một mã tùy chọn để nhắc nhở các thí sinh trước khi họ được phép tham gia kỳ " "thi. Để trống để vô hiệu hóa." -#: judge/models/contest.py:168 judge/models/problem.py:191 +#: judge/models/contest.py:163 judge/models/problem.py:191 msgid "personae non gratae" msgstr "các người dùng bị cấm" -#: judge/models/contest.py:169 +#: judge/models/contest.py:164 msgid "Bans the selected users from joining this contest." msgstr "Cấm những người dùng đã chọn tham gia kỳ thi này." -#: judge/models/contest.py:170 +#: judge/models/contest.py:165 msgid "Banned judges" msgstr "Cấm máy chấm" -#: judge/models/contest.py:171 +#: judge/models/contest.py:166 msgid "Bans the selected judges from judging this contest." msgstr "Các máy chấm không được phép chấm kỳ thi này" -#: judge/models/contest.py:172 +#: judge/models/contest.py:167 msgid "contest format" msgstr "định dạng kỳ thi" -#: judge/models/contest.py:173 +#: judge/models/contest.py:168 msgid "The contest format module to use." msgstr "Dạng mô đun sử dụng cho kỳ thi." -#: judge/models/contest.py:174 +#: judge/models/contest.py:169 msgid "contest format configuration" msgstr "cấu hình dạng kỳ thi" -#: judge/models/contest.py:175 +#: judge/models/contest.py:170 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 " @@ -1556,310 +1472,310 @@ msgstr "" "trống nếu không dùng. Định dạng chính xác phụ thuộc vào định dạng kỳ thi " "được chọn." -#: judge/models/contest.py:178 +#: judge/models/contest.py:173 msgid "contest problem label script" msgstr "" -#: judge/models/contest.py:179 +#: judge/models/contest.py:174 msgid "" "A custom Lua function to generate problem labels. Requires a single function " "with an integer parameter, the zero-indexed contest problem index, and " "returns a string, the label." msgstr "" -#: judge/models/contest.py:182 +#: judge/models/contest.py:177 msgid "contest lock" msgstr "" -#: judge/models/contest.py:183 +#: judge/models/contest.py:178 msgid "" "Prevent submissions from this contest from being rejudged after this date." msgstr "Các bài nộp cho kỳ thi này sau ngày này sẽ không thể được chấm lại." -#: judge/models/contest.py:185 +#: judge/models/contest.py:180 msgid "precision points" msgstr "" -#: judge/models/contest.py:187 +#: judge/models/contest.py:182 msgid "Number of digits to round points to." msgstr "" -#: judge/models/contest.py:188 +#: judge/models/contest.py:183 msgid "official ranking" msgstr "" -#: judge/models/contest.py:189 +#: judge/models/contest.py:184 msgid "Official ranking exported from CMS in CSV format." msgstr "" -#: judge/models/contest.py:190 judge/models/profile.py:237 +#: judge/models/contest.py:185 judge/models/profile.py:229 msgid "last data download time" msgstr "thời gian tải gần nhất" -#: judge/models/contest.py:191 +#: judge/models/contest.py:186 msgid "Disallow virtual joining" msgstr "Không cho phép tham gia ảo" -#: judge/models/contest.py:192 +#: judge/models/contest.py:187 msgid "Disallow virtual joining after contest has ended." msgstr "Không cho phép tham gia ảo sau khi kỳ thi kết thúc." -#: judge/models/contest.py:195 +#: judge/models/contest.py:190 msgid "ranking access code" msgstr "mã truy cập bảng xếp hạng" -#: judge/models/contest.py:196 +#: judge/models/contest.py:191 msgid "" "An optional code to view the contest ranking. Leave it blank to disable." msgstr "Một mã tùy chọn để xem bảng xếp hạng. Để trống để vô hiệu hóa." -#: judge/models/contest.py:536 +#: judge/models/contest.py:531 msgid "See private contests" msgstr "Xem các kỳ thi riêng tư" -#: judge/models/contest.py:537 +#: judge/models/contest.py:532 msgid "Edit own contests" msgstr "Sửa các kỳ thi sở hữu" -#: judge/models/contest.py:538 +#: judge/models/contest.py:533 msgid "Edit all contests" msgstr "Sửa tất cả các kỳ thi" -#: judge/models/contest.py:539 +#: judge/models/contest.py:534 msgid "Clone contest" msgstr "Nhân bản kỳ thi" -#: judge/models/contest.py:540 templates/contest/moss.html:75 +#: judge/models/contest.py:535 templates/contest/moss.html:75 msgid "MOSS contest" msgstr "MOSS kỳ thi" -#: judge/models/contest.py:541 +#: judge/models/contest.py:536 msgid "Rate contests" msgstr "Đánh gia các kỳ thi" -#: judge/models/contest.py:542 +#: judge/models/contest.py:537 msgid "Contest access codes" msgstr "Mã truy cập kỳ thi" -#: judge/models/contest.py:543 +#: judge/models/contest.py:538 msgid "Create private contests" msgstr "Tạo kỳ thi riêng tư" -#: judge/models/contest.py:544 +#: judge/models/contest.py:539 msgid "Change contest visibility" msgstr "" -#: judge/models/contest.py:545 +#: judge/models/contest.py:540 msgid "Edit contest problem label script" msgstr "" -#: judge/models/contest.py:546 +#: judge/models/contest.py:541 msgid "Change lock status of contest" msgstr "" -#: judge/models/contest.py:548 judge/models/contest.py:697 -#: judge/models/contest.py:736 judge/models/contest.py:760 +#: judge/models/contest.py:543 judge/models/contest.py:692 +#: judge/models/contest.py:731 judge/models/contest.py:755 #: judge/models/submission.py:94 msgid "contest" msgstr "kỳ thi" -#: judge/models/contest.py:549 +#: judge/models/contest.py:544 msgid "contests" msgstr "kỳ thi" -#: judge/models/contest.py:553 +#: judge/models/contest.py:548 msgid "announced contest" msgstr "kỳ thi được thông báo" -#: judge/models/contest.py:554 +#: judge/models/contest.py:549 msgid "announcement title" msgstr "tiêu đề thông báo" -#: judge/models/contest.py:555 +#: judge/models/contest.py:550 msgid "announcement body" msgstr "nội dung thông báo" -#: judge/models/contest.py:556 +#: judge/models/contest.py:551 msgid "announcement timestamp" msgstr "thời điểm thông báo" -#: judge/models/contest.py:570 +#: judge/models/contest.py:565 msgid "associated contest" msgstr "kỳ thi liên quan" -#: judge/models/contest.py:573 +#: judge/models/contest.py:568 msgid "score" msgstr "điểm" -#: judge/models/contest.py:574 +#: judge/models/contest.py:569 msgid "cumulative time" msgstr "thời gian tích lũy" -#: judge/models/contest.py:575 +#: judge/models/contest.py:570 msgid "frozen score" msgstr "" -#: judge/models/contest.py:576 +#: judge/models/contest.py:571 msgid "Frozen score in the scoreboard." msgstr "" -#: judge/models/contest.py:577 +#: judge/models/contest.py:572 msgid "frozen cumulative time" msgstr "" -#: judge/models/contest.py:578 +#: judge/models/contest.py:573 msgid "Frozen cumulative time in the scoreboard." msgstr "" -#: judge/models/contest.py:579 +#: judge/models/contest.py:574 msgid "is disqualified" msgstr "bị loại" -#: judge/models/contest.py:580 +#: judge/models/contest.py:575 msgid "Whether this participation is disqualified." msgstr "có bị hủy tư cách tham gia" -#: judge/models/contest.py:581 +#: judge/models/contest.py:576 msgid "tie-breaking field" msgstr "" -#: judge/models/contest.py:582 +#: judge/models/contest.py:577 msgid "frozen tie-breaking field" msgstr "" -#: judge/models/contest.py:583 +#: judge/models/contest.py:578 msgid "virtual participation id" msgstr "mã số tham gia ảo" -#: judge/models/contest.py:584 +#: judge/models/contest.py:579 msgid "0 means non-virtual, otherwise the n-th virtual participation." msgstr "0 nghĩa là tham gia trực tiếp, ngược lại là lần đăng ký ảo thứ n." -#: judge/models/contest.py:585 +#: judge/models/contest.py:580 msgid "contest format specific data" msgstr "định dạng dữ liệu của kỳ thi" -#: judge/models/contest.py:681 +#: judge/models/contest.py:676 #, python-format msgid "%(user)s spectating in %(contest)s" msgstr "%(user)s spectating trong %(contest)s" -#: judge/models/contest.py:683 +#: judge/models/contest.py:678 #, python-format msgid "%(user)s in %(contest)s, v%(id)d" msgstr "%(user)s trong %(contest)s, v%(id)d" -#: judge/models/contest.py:686 +#: judge/models/contest.py:681 #, python-format msgid "%(user)s in %(contest)s" msgstr "%(user)s trong %(contest)s" -#: judge/models/contest.py:689 +#: judge/models/contest.py:684 msgid "contest participation" msgstr "tham gia kỳ thi" -#: judge/models/contest.py:690 +#: judge/models/contest.py:685 msgid "contest participations" msgstr "tham gia kỳ thi" -#: judge/models/contest.py:696 judge/models/contest.py:720 -#: judge/models/contest.py:761 judge/models/problem.py:629 -#: judge/models/problem.py:634 judge/models/problem.py:653 +#: judge/models/contest.py:691 judge/models/contest.py:715 +#: judge/models/contest.py:756 judge/models/problem.py:628 +#: judge/models/problem.py:633 judge/models/problem.py:652 #: judge/models/problem_data.py:56 msgid "problem" msgstr "vấn đề" -#: judge/models/contest.py:698 judge/models/contest.py:724 +#: judge/models/contest.py:693 judge/models/contest.py:719 #: judge/models/problem.py:178 msgid "points" msgstr "điểm" -#: judge/models/contest.py:699 +#: judge/models/contest.py:694 msgid "partial" msgstr "một phần" -#: judge/models/contest.py:700 judge/models/contest.py:725 +#: judge/models/contest.py:695 judge/models/contest.py:720 msgid "is pretested" msgstr "có pretest?" -#: judge/models/contest.py:701 judge/models/interface.py:45 +#: judge/models/contest.py:696 judge/models/interface.py:45 msgid "order" msgstr "thứ tự" -#: judge/models/contest.py:702 +#: judge/models/contest.py:697 msgid "output prefix length override" msgstr "ghi đè độ dài output prefix" -#: judge/models/contest.py:704 +#: judge/models/contest.py:699 msgid "" "Maximum number of submissions for this problem, or leave blank for no limit." msgstr "" -#: judge/models/contest.py:707 +#: judge/models/contest.py:702 msgid "Why include a problem you can't submit to?" msgstr "Tại sao lại thêm bài mà bạn không thể nộp?" -#: judge/models/contest.py:712 +#: judge/models/contest.py:707 msgid "contest problem" msgstr "bài của kỳ thi" -#: judge/models/contest.py:713 +#: judge/models/contest.py:708 msgid "contest problems" msgstr "bài của kỳ thi" -#: judge/models/contest.py:718 judge/models/submission.py:260 +#: judge/models/contest.py:713 judge/models/submission.py:259 msgid "submission" msgstr "nộp bài" -#: judge/models/contest.py:722 judge/models/contest.py:737 +#: judge/models/contest.py:717 judge/models/contest.py:732 msgid "participation" msgstr "tham dự" -#: judge/models/contest.py:726 +#: judge/models/contest.py:721 msgid "Whether this submission was ran only on pretests." msgstr "Bài nộp này chỉ chạy trên pretest." -#: judge/models/contest.py:730 +#: judge/models/contest.py:725 msgid "contest submission" msgstr "bài nộp của contest" -#: judge/models/contest.py:731 +#: judge/models/contest.py:726 msgid "contest submissions" msgstr "bài nộp của contest" -#: judge/models/contest.py:739 +#: judge/models/contest.py:734 msgid "rank" msgstr "hạng" -#: judge/models/contest.py:740 +#: judge/models/contest.py:735 msgid "rating" msgstr "" -#: judge/models/contest.py:741 +#: judge/models/contest.py:736 msgid "raw rating" msgstr "" -#: judge/models/contest.py:742 +#: judge/models/contest.py:737 msgid "contest performance" msgstr "" -#: judge/models/contest.py:743 +#: judge/models/contest.py:738 msgid "last rated" msgstr "lần xếp hạng cuối" -#: judge/models/contest.py:747 +#: judge/models/contest.py:742 msgid "contest rating" msgstr "xếp hạng kỳ thi" -#: judge/models/contest.py:748 +#: judge/models/contest.py:743 msgid "contest ratings" msgstr "xếp hạng kỳ thi" -#: judge/models/contest.py:768 +#: judge/models/contest.py:763 msgid "contest moss result" msgstr "kết quả moss" -#: judge/models/contest.py:769 +#: judge/models/contest.py:764 msgid "contest moss results" msgstr "kết quả moss" @@ -1911,7 +1827,7 @@ msgstr "tiêu đề bài viết" msgid "slug" msgstr "slug" -#: judge/models/interface.py:71 judge/models/problem.py:671 +#: judge/models/interface.py:71 judge/models/problem.py:670 msgid "public visibility" msgstr "hiển thị công khai" @@ -1939,9 +1855,9 @@ msgstr "bài đăng chung" msgid "Display this blog post at the homepage." msgstr "Hiển thị bài đăng này ở trang chủ." -#: judge/models/interface.py:80 judge/models/profile.py:149 -#: judge/models/profile.py:154 judge/models/profile.py:194 -#: judge/models/profile.py:500 +#: judge/models/interface.py:80 judge/models/profile.py:141 +#: judge/models/profile.py:146 judge/models/profile.py:186 +#: judge/models/profile.py:492 msgid "organization" msgstr "tổ chức" @@ -1971,6 +1887,9 @@ msgstr "bài đăng blog" msgid "blog posts" msgstr "Bài đăng blog" +msgid "Loading..." +msgstr "Đang tải..." + #: judge/models/interface.py:145 msgid "blog vote" msgstr "bỏ phiếu blog" @@ -2168,7 +2087,7 @@ msgstr "" "Giới hạn thời gian (tính bằng giây) cho bài tập này. Phần lẻ giây (chẳng hạn " "1.5) cũng được hỗ trợ." -#: judge/models/problem.py:172 judge/models/problem.py:658 +#: judge/models/problem.py:172 judge/models/problem.py:657 msgid "memory limit" msgstr "giới hạn bộ nhớ" @@ -2293,7 +2212,7 @@ msgstr "Đề xuất bài mới" msgid "Edit problems with full markup" msgstr "" -#: judge/models/problem.py:621 templates/problem/problem.html:271 +#: judge/models/problem.py:621 templates/problem/problem.html:197 msgid "Clone problem" msgstr "Nhân bản bài" @@ -2317,67 +2236,63 @@ msgstr "" msgid "Import Codeforces Polygon package" msgstr "" -#: judge/models/problem.py:627 -msgid "Edit type and group for all problems" -msgstr "" - -#: judge/models/problem.py:636 +#: judge/models/problem.py:635 msgid "translated name" msgstr "tên bộ dịch" -#: judge/models/problem.py:637 +#: judge/models/problem.py:636 msgid "translated description" msgstr "mô tả bộ dịch" -#: judge/models/problem.py:642 +#: judge/models/problem.py:641 msgid "problem translation" msgstr "dịch đầu bài" -#: judge/models/problem.py:643 +#: judge/models/problem.py:642 msgid "problem translations" msgstr "dịch đầu bài" -#: judge/models/problem.py:647 +#: judge/models/problem.py:646 msgid "clarified problem" msgstr "bài tập được làm rõ" -#: judge/models/problem.py:648 +#: judge/models/problem.py:647 msgid "clarification body" msgstr "nội dung làm rõ" -#: judge/models/problem.py:649 +#: judge/models/problem.py:648 msgid "clarification timestamp" msgstr "thời gian làm rõ" -#: judge/models/problem.py:664 +#: judge/models/problem.py:663 msgid "language-specific resource limit" msgstr "giới hạn theo ngôn ngữ" -#: judge/models/problem.py:665 +#: judge/models/problem.py:664 msgid "language-specific resource limits" msgstr "giới hạn theo ngôn ngữ" -#: judge/models/problem.py:669 +#: judge/models/problem.py:668 msgid "associated problem" msgstr "bài liên quan" -#: judge/models/problem.py:672 +#: judge/models/problem.py:671 msgid "publish date" msgstr "ngày công bố" -#: judge/models/problem.py:674 +#: judge/models/problem.py:673 msgid "editorial content" msgstr "nội dung lời giải" -#: judge/models/problem.py:697 +#: judge/models/problem.py:696 msgid "See hidden solutions" msgstr "Xem lời giải ẩn" -#: judge/models/problem.py:699 +#: judge/models/problem.py:698 msgid "solution" msgstr "lời giải" -#: judge/models/problem.py:700 +#: judge/models/problem.py:699 msgid "solutions" msgstr "lời giải" @@ -2635,333 +2550,319 @@ msgid "" msgstr "" "Ảnh này sẽ thay thế logo mặc định của trang khi thành viên xem tổ chức." -#: judge/models/profile.py:69 -msgid "Remaining purchased credits" -msgstr "" - -#: judge/models/profile.py:72 -msgid "Remaining free credits for the current month" -msgstr "" - -#: judge/models/profile.py:77 -#, fuzzy -#| msgid "Available free credits (reset each month): " -msgid "Amount of free credits allocated each month" -msgstr "Số giờ chấm bài miễn phí còn lại trong tháng: " - -#: judge/models/profile.py:144 +#: judge/models/profile.py:136 msgid "Administer organizations" msgstr "" -#: judge/models/profile.py:145 +#: judge/models/profile.py:137 msgid "Edit all organizations" msgstr "Chỉnh sửa toàn bộ tổ chức" -#: judge/models/profile.py:146 +#: judge/models/profile.py:138 msgid "Change is_open field" msgstr "Chỉnh sửa is_open field" -#: judge/models/profile.py:147 +#: judge/models/profile.py:139 msgid "Create organization without limit" msgstr "Không giới hạn tạo tổ chức" -#: judge/models/profile.py:157 +#: judge/models/profile.py:149 msgid "consumed credit" msgstr "" -#: judge/models/profile.py:160 +#: judge/models/profile.py:152 #, fuzzy #| msgid "organization slug" msgid "organization monthly usage" msgstr "tên viết tắt trên đường dẫn" -#: judge/models/profile.py:161 +#: judge/models/profile.py:153 #, fuzzy #| msgid "organization join request" msgid "organization monthly usages" msgstr "yêu cầu tham gia tổ chức" -#: judge/models/profile.py:166 +#: judge/models/profile.py:158 msgid "badge name" msgstr "" -#: judge/models/profile.py:167 +#: judge/models/profile.py:159 msgid "mini badge URL" msgstr "" -#: judge/models/profile.py:168 +#: judge/models/profile.py:160 msgid "full size badge URL" msgstr "" -#: judge/models/profile.py:175 +#: judge/models/profile.py:167 msgid "user associated" msgstr "người sử dụng tương ứng" -#: judge/models/profile.py:176 +#: judge/models/profile.py:168 msgid "self-description" msgstr "tự mô tả" -#: judge/models/profile.py:177 +#: judge/models/profile.py:169 msgid "time zone" msgstr "múi giờ" -#: judge/models/profile.py:179 +#: judge/models/profile.py:171 msgid "preferred language" msgstr "ngôn ngữ" -#: judge/models/profile.py:186 +#: judge/models/profile.py:178 msgid "Ace theme" msgstr "" -#: judge/models/profile.py:187 +#: judge/models/profile.py:179 #, fuzzy #| msgid "Editor theme:" msgid "site theme" msgstr "Giao diện khung code:" -#: judge/models/profile.py:188 +#: judge/models/profile.py:180 msgid "last access time" msgstr "lần truy cập cuối cùng" -#: judge/models/profile.py:189 +#: judge/models/profile.py:181 msgid "last IP" msgstr "IP" -#: judge/models/profile.py:190 +#: judge/models/profile.py:182 #, fuzzy #| msgid "authentication key" msgid "IP-based authentication" msgstr "mã xác thực" -#: judge/models/profile.py:192 +#: judge/models/profile.py:184 msgid "badges" msgstr "huy hiệu" -#: judge/models/profile.py:193 +#: judge/models/profile.py:185 msgid "display badge" msgstr "huy hiệu hiển thị" -#: judge/models/profile.py:196 +#: judge/models/profile.py:188 msgid "display rank" msgstr "hiển thị xếp hạng" -#: judge/models/profile.py:198 +#: judge/models/profile.py:190 msgid "comment mute" msgstr "tắt bình luận" -#: judge/models/profile.py:198 +#: judge/models/profile.py:190 msgid "Some users are at their best when silent." msgstr "Một vài người tốt nhất là khi im lặng." -#: judge/models/profile.py:200 +#: judge/models/profile.py:192 msgid "unlisted user" msgstr "thành viên không được liệt kê" -#: judge/models/profile.py:200 +#: judge/models/profile.py:192 msgid "User will not be ranked." msgstr "Thành viên không được xếp hạng." -#: judge/models/profile.py:203 +#: judge/models/profile.py:195 msgid "Show to banned user in login page." msgstr "" -#: judge/models/profile.py:204 +#: judge/models/profile.py:196 msgid "Allow tagging" msgstr "Cho phép tag bài" -#: judge/models/profile.py:205 +#: judge/models/profile.py:197 msgid "User will be allowed to tag problems." msgstr "Người dùng có thể tag bài." -#: judge/models/profile.py:208 +#: judge/models/profile.py:200 msgid "user script" msgstr "script tự định nghĩa" -#: judge/models/profile.py:209 +#: judge/models/profile.py:201 msgid "User-defined JavaScript for site customization." msgstr "JavaScript tự định nghĩa bởi người dùng để tùy chỉnh." -#: judge/models/profile.py:210 +#: judge/models/profile.py:202 msgid "current contest" msgstr "kỳ thi hiện tại" -#: judge/models/profile.py:212 +#: judge/models/profile.py:204 msgid "math engine" msgstr "" -#: judge/models/profile.py:214 +#: judge/models/profile.py:206 msgid "The rendering engine used to render math." msgstr "chọn công cụ để hiện công thức toán" -#: judge/models/profile.py:215 +#: judge/models/profile.py:207 msgid "TOTP 2FA enabled" msgstr "" -#: judge/models/profile.py:216 +#: judge/models/profile.py:208 msgid "Check to enable TOTP-based two-factor authentication." msgstr "" -#: judge/models/profile.py:217 +#: judge/models/profile.py:209 msgid "WebAuthn 2FA enabled" msgstr "" -#: judge/models/profile.py:218 +#: judge/models/profile.py:210 msgid "Check to enable WebAuthn-based two-factor authentication." msgstr "" -#: judge/models/profile.py:219 +#: judge/models/profile.py:211 msgid "TOTP key" msgstr "Mã TOTP" -#: judge/models/profile.py:220 +#: judge/models/profile.py:212 msgid "32-character Base32-encoded key for TOTP." msgstr "mã 32 ký tự base32-encoded cho TOTP" -#: judge/models/profile.py:222 +#: judge/models/profile.py:214 msgid "TOTP key must be empty or Base32." msgstr "Mã TOTP cần rỗng hoặc base32" -#: judge/models/profile.py:223 +#: judge/models/profile.py:215 msgid "scratch codes" msgstr "" -#: judge/models/profile.py:224 +#: judge/models/profile.py:216 msgid "JSON array of 16-character Base32-encoded codes for scratch codes." msgstr "" -#: judge/models/profile.py:228 +#: judge/models/profile.py:220 msgid "" "Scratch codes must be empty or a JSON array of 16-character Base32 codes." msgstr "" -#: judge/models/profile.py:230 +#: judge/models/profile.py:222 msgid "last TOTP timecode" msgstr "" -#: judge/models/profile.py:231 +#: judge/models/profile.py:223 msgid "API token" msgstr "" -#: judge/models/profile.py:232 +#: judge/models/profile.py:224 msgid "64-character hex-encoded API access token." msgstr "" -#: judge/models/profile.py:234 +#: judge/models/profile.py:226 msgid "API token must be None or hexadecimal" msgstr "" -#: judge/models/profile.py:235 +#: judge/models/profile.py:227 msgid "internal notes" msgstr "ghi chú nội bộ" -#: judge/models/profile.py:236 +#: judge/models/profile.py:228 msgid "Notes for administrators regarding this user." msgstr "Ghi chú cho quản trị viên chấm lại cho thành viên này." -#: judge/models/profile.py:238 +#: judge/models/profile.py:230 msgid "display name override" msgstr "" -#: judge/models/profile.py:239 +#: judge/models/profile.py:231 msgid "Name displayed in place of username." msgstr "" -#: judge/models/profile.py:448 +#: judge/models/profile.py:440 msgid "Shows in-progress development stuff" msgstr "" -#: judge/models/profile.py:449 +#: judge/models/profile.py:441 msgid "Edit TOTP settings" msgstr "" -#: judge/models/profile.py:450 +#: judge/models/profile.py:442 msgid "Can upload image directly to server via martor" msgstr "" -#: judge/models/profile.py:451 +#: judge/models/profile.py:443 msgid "Can set high problem timelimit" msgstr "" -#: judge/models/profile.py:452 +#: judge/models/profile.py:444 msgid "Can set long contest duration" msgstr "" -#: judge/models/profile.py:453 +#: judge/models/profile.py:445 msgid "Can create unlimitted number of testcases for a problem" msgstr "" -#: judge/models/profile.py:454 +#: judge/models/profile.py:446 #, fuzzy #| msgid "Ban this user" msgid "Ban users" msgstr "Ban người dùng này" -#: judge/models/profile.py:456 +#: judge/models/profile.py:448 msgid "user profile" msgstr "hồ sơ người dùng" -#: judge/models/profile.py:457 +#: judge/models/profile.py:449 msgid "user profiles" msgstr "hồ sơ người dùng" -#: judge/models/profile.py:470 +#: judge/models/profile.py:462 msgid "device name" msgstr "" -#: judge/models/profile.py:471 +#: judge/models/profile.py:463 msgid "credential ID" msgstr "" -#: judge/models/profile.py:472 +#: judge/models/profile.py:464 msgid "public key" msgstr "" -#: judge/models/profile.py:473 +#: judge/models/profile.py:465 msgid "sign counter" msgstr "" -#: judge/models/profile.py:491 +#: judge/models/profile.py:483 #, python-format msgid "WebAuthn credential: %(name)s" msgstr "" -#: judge/models/profile.py:494 +#: judge/models/profile.py:486 msgid "WebAuthn credential" msgstr "" -#: judge/models/profile.py:495 +#: judge/models/profile.py:487 msgid "WebAuthn credentials" msgstr "" -#: judge/models/profile.py:502 +#: judge/models/profile.py:494 msgid "request time" msgstr "thời gian yêu cầu" -#: judge/models/profile.py:503 +#: judge/models/profile.py:495 msgid "state" msgstr "trạng thái" -#: judge/models/profile.py:504 templates/organization/requests/tabs.html:4 +#: judge/models/profile.py:496 templates/organization/requests/tabs.html:4 msgid "Pending" msgstr "Đang chờ" -#: judge/models/profile.py:505 templates/organization/requests/tabs.html:10 +#: judge/models/profile.py:497 templates/organization/requests/tabs.html:10 msgid "Approved" msgstr "Phê duyệt" -#: judge/models/profile.py:506 templates/organization/requests/tabs.html:13 +#: judge/models/profile.py:498 templates/organization/requests/tabs.html:13 msgid "Rejected" msgstr "Bị từ chối" -#: judge/models/profile.py:508 +#: judge/models/profile.py:500 msgid "reason" msgstr "lý do" -#: judge/models/profile.py:511 +#: judge/models/profile.py:503 msgid "organization join request" msgstr "yêu cầu tham gia tổ chức" -#: judge/models/profile.py:512 +#: judge/models/profile.py:504 msgid "organization join requests" msgstr "yêu cầu tham gia tổ chức" @@ -3147,46 +3048,34 @@ msgid "Whether this judge should be removed from judging queue." msgstr "" #: judge/models/runtime.py:143 -#, fuzzy -#| msgid "judge name" -msgid "judge tier" -msgstr "tên máy chấm" - -#: judge/models/runtime.py:144 -msgid "" -"The tier of this judge. Only online judges of the minimum tier will be used. " -"This is used for high-availability." -msgstr "" - -#: judge/models/runtime.py:146 msgid "judge online status" msgstr "trạng thái online của máy chấm" -#: judge/models/runtime.py:147 +#: judge/models/runtime.py:144 msgid "judge start time" msgstr "thời gian bắt đầu chấm" -#: judge/models/runtime.py:148 +#: judge/models/runtime.py:145 msgid "response time" msgstr "thời gian đáp ứng" -#: judge/models/runtime.py:149 +#: judge/models/runtime.py:146 msgid "system load" msgstr "mức tải của hệ thống" -#: judge/models/runtime.py:150 +#: judge/models/runtime.py:147 msgid "Load for the last minute, divided by processors to be fair." msgstr "Mức tải ở phút vừa qua, chia cho số bộ vi xử lý." -#: judge/models/runtime.py:152 +#: judge/models/runtime.py:149 msgid "last connected IP" msgstr "" -#: judge/models/runtime.py:154 judge/models/runtime.py:203 +#: judge/models/runtime.py:151 judge/models/runtime.py:200 msgid "judges" msgstr "các máy chấm" -#: judge/models/runtime.py:202 +#: judge/models/runtime.py:199 msgid "judge" msgstr "máy chấm" @@ -3262,15 +3151,15 @@ msgstr "Lỗi nội bộ (máy chủ chấm bài lỗi)" msgid "submission time" msgstr "thời điểm nộp bài" -#: judge/models/submission.py:76 judge/models/submission.py:307 +#: judge/models/submission.py:76 judge/models/submission.py:306 msgid "execution time" msgstr "thời gian chạy tối đa" -#: judge/models/submission.py:77 judge/models/submission.py:308 +#: judge/models/submission.py:77 judge/models/submission.py:307 msgid "memory usage" msgstr "bộ nhớ sử dụng" -#: judge/models/submission.py:78 judge/models/submission.py:309 +#: judge/models/submission.py:78 judge/models/submission.py:308 msgid "points granted" msgstr "điểm được cho" @@ -3322,89 +3211,89 @@ msgstr "chỉ được chạy pretest" msgid "submission lock" msgstr "khoá bài nộp" -#: judge/models/submission.py:227 +#: judge/models/submission.py:226 #, python-format msgid "Submission %(id)d of %(problem)s by %(user)s" msgstr "Bài nộp %(id)d cho %(problem)s của %(user)s" -#: judge/models/submission.py:252 +#: judge/models/submission.py:251 msgid "Abort any submission" msgstr "Ngưng chấm bài nộp" -#: judge/models/submission.py:253 +#: judge/models/submission.py:252 msgid "Rejudge the submission" msgstr "Chấm lại bài nộp" -#: judge/models/submission.py:254 +#: judge/models/submission.py:253 msgid "Rejudge a lot of submissions" msgstr "Chấm lại nhiều bài nộp" -#: judge/models/submission.py:255 +#: judge/models/submission.py:254 msgid "Submit without limit" msgstr "Nộp bài không giới hạn" -#: judge/models/submission.py:256 +#: judge/models/submission.py:255 msgid "View all submission" msgstr "Xem tất cả bài nộp" -#: judge/models/submission.py:257 +#: judge/models/submission.py:256 msgid "Resubmit others' submission" msgstr "Nộp lại bài nộp của người khác" -#: judge/models/submission.py:258 +#: judge/models/submission.py:257 msgid "Change lock status of submission" msgstr "Đổi trạng thái khoá của bài nộp" -#: judge/models/submission.py:261 +#: judge/models/submission.py:260 msgid "submissions" msgstr "bài nộp" -#: judge/models/submission.py:291 judge/models/submission.py:303 +#: judge/models/submission.py:290 judge/models/submission.py:302 msgid "associated submission" msgstr "bài nộp liên quan" -#: judge/models/submission.py:293 +#: judge/models/submission.py:292 msgid "source code" msgstr "mã nguồn" -#: judge/models/submission.py:296 +#: judge/models/submission.py:295 #, python-format msgid "Source of %(submission)s" msgstr "Mã nguồn của %(submission)s" -#: judge/models/submission.py:305 +#: judge/models/submission.py:304 msgid "test case ID" msgstr "mã testcase" -#: judge/models/submission.py:306 +#: judge/models/submission.py:305 msgid "status flag" msgstr "cờ trạng thái" -#: judge/models/submission.py:310 +#: judge/models/submission.py:309 msgid "points possible" msgstr "khả năng điểm" -#: judge/models/submission.py:311 +#: judge/models/submission.py:310 msgid "batch number" msgstr "nhóm test số" -#: judge/models/submission.py:312 +#: judge/models/submission.py:311 msgid "judging feedback" msgstr "phản hồi từ trình chấm" -#: judge/models/submission.py:313 +#: judge/models/submission.py:312 msgid "extended judging feedback" msgstr "phản hồi đầy đủ từ trình chấm" -#: judge/models/submission.py:314 +#: judge/models/submission.py:313 msgid "program output" msgstr "output của chương trình" -#: judge/models/submission.py:328 +#: judge/models/submission.py:327 msgid "submission test case" msgstr "test case của bài" -#: judge/models/submission.py:329 +#: judge/models/submission.py:328 msgid "submission test cases" msgstr "các test case của bài" @@ -3508,19 +3397,19 @@ msgstr "nội dung" msgid "message time" msgstr "" -#: judge/tasks/contest.py:29 +#: judge/tasks/contest.py:27 msgid "Recalculating contest scores" msgstr "Tính lại điểm của kỳ thi" -#: judge/tasks/contest.py:50 +#: judge/tasks/contest.py:48 msgid "Running MOSS" msgstr "" -#: judge/tasks/contest.py:92 judge/tasks/user.py:49 +#: judge/tasks/contest.py:87 judge/tasks/user.py:49 msgid "Applying filters" msgstr "Đang lọc dữ liệu" -#: judge/tasks/contest.py:120 +#: judge/tasks/contest.py:115 msgid "Preparing contest data" msgstr "Đang chuẩn bị dữ liệu kỳ thi" @@ -3693,23 +3582,23 @@ msgstr "" msgid "Page %d of Posts" msgstr "Blog - Trang %d" -#: judge/views/blog.py:279 judge/views/blog.py:282 +#: judge/views/blog.py:278 judge/views/blog.py:281 msgid "Creating new blog post" msgstr "Tạo blog mới" -#: judge/views/blog.py:297 judge/views/contests.py:1277 -#: judge/views/organization.py:393 judge/views/organization.py:697 -#: judge/views/organization.py:721 judge/views/problem.py:905 -#: judge/views/problem.py:945 judge/views/tag.py:182 +#: judge/views/blog.py:296 judge/views/contests.py:1272 +#: judge/views/organization.py:377 judge/views/organization.py:682 +#: judge/views/organization.py:706 judge/views/problem.py:842 +#: judge/views/problem.py:882 judge/views/tag.py:182 msgid "Created on site" msgstr "Tạo trên trang web" -#: judge/views/blog.py:310 judge/views/blog.py:348 judge/views/contests.py:272 -#: judge/views/contests.py:1288 +#: judge/views/blog.py:309 judge/views/blog.py:347 judge/views/contests.py:272 +#: judge/views/contests.py:1283 msgid "Permission denied" msgstr "Từ chối quyền" -#: judge/views/blog.py:311 +#: judge/views/blog.py:310 #, python-format msgid "" "You cannot create blog post.\n" @@ -3718,17 +3607,17 @@ msgstr "" "Không thể tạo blog.\n" "Lưu ý: Bạn cần giải ít nhất %d bài để có thể tạo blog." -#: judge/views/blog.py:324 judge/views/blog.py:327 +#: judge/views/blog.py:323 judge/views/blog.py:326 msgid "Updating blog post" msgstr "Cập nhật blog" -#: judge/views/blog.py:342 judge/views/comment.py:151 -#: judge/views/contests.py:1349 judge/views/organization.py:436 -#: judge/views/problem.py:1099 +#: judge/views/blog.py:341 judge/views/comment.py:142 +#: judge/views/contests.py:1344 judge/views/organization.py:423 +#: judge/views/problem.py:1036 msgid "Edited from site" msgstr "Chỉnh sửa từ trang web" -#: judge/views/blog.py:349 +#: judge/views/blog.py:348 msgid "You cannot edit blog post." msgstr "Bạn không thể chỉnh sửa blog." @@ -3740,7 +3629,7 @@ msgstr "Không tìm thấy bình luận." msgid "You cannot vote on your own comments." msgstr "Bạn không thể tự đánh giá bình luận của mình." -#: judge/views/comment.py:176 +#: judge/views/comment.py:167 msgid "Editing comment" msgstr "Đang chỉnh sửa bình luận" @@ -3754,7 +3643,7 @@ msgstr "Không có kỳ thi nào" msgid "Could not find a contest with the key \"%s\"." msgstr "Không thể tìm thấy kỳ thi với khóa \"%s\"." -#: judge/views/contests.py:97 templates/base.html:268 +#: judge/views/contests.py:97 templates/base.html:248 msgid "Contests" msgstr "Các kỳ thi" @@ -3776,7 +3665,7 @@ msgid "You are not allowed to clone contests." msgstr "Bạn không có quyền nhân bản kỳ thi." #: judge/views/contests.py:384 judge/views/contests.py:431 -#: judge/views/contests.py:1299 judge/views/contests.py:1367 +#: judge/views/contests.py:1294 judge/views/contests.py:1362 msgid "You are not allowed to edit this contest." msgstr "Bạn không có quyền chỉnh sửa kỳ thi này." @@ -3898,93 +3787,93 @@ msgstr "" msgid "%s Statistics" msgstr "%s Thống kê" -#: judge/views/contests.py:978 +#: judge/views/contests.py:973 #, python-format msgid "%s Rankings" msgstr "Bảng xếp hạng của %s" -#: judge/views/contests.py:1022 +#: judge/views/contests.py:1017 msgid "???" msgstr "" -#: judge/views/contests.py:1060 +#: judge/views/contests.py:1055 msgid "Ranking access code required" msgstr "" -#: judge/views/contests.py:1061 +#: judge/views/contests.py:1056 msgid "You need to provide a valid ranking access code to access this page." msgstr "" -#: judge/views/contests.py:1072 +#: judge/views/contests.py:1067 #, python-format msgid "%s Official Rankings" msgstr "Bảng xếp hạng chính thức của %s" -#: judge/views/contests.py:1114 +#: judge/views/contests.py:1109 #, python-format msgid "Your participation in %(contest)s" msgstr "Các lần tham gia %(contest)s của bạn" -#: judge/views/contests.py:1115 +#: judge/views/contests.py:1110 #, python-format msgid "%(user)s's participation in %(contest)s" msgstr "Các lần tham gia %(contest)s của %(user)s" -#: judge/views/contests.py:1124 +#: judge/views/contests.py:1119 msgid "Live" msgstr "Trực tuyến" -#: judge/views/contests.py:1135 templates/contest/contest-tabs.html:16 +#: judge/views/contests.py:1130 templates/contest/contest-tabs.html:16 msgid "Participation" msgstr "Tham gia" -#: judge/views/contests.py:1167 +#: judge/views/contests.py:1162 msgid "You are not allowed to run MOSS." msgstr "Bạn không có quyền chạy MOSS." -#: judge/views/contests.py:1180 +#: judge/views/contests.py:1175 #, python-format msgid "%s MOSS Results" msgstr "" -#: judge/views/contests.py:1207 +#: judge/views/contests.py:1202 #, python-format msgid "Running MOSS for %s..." msgstr "" -#: judge/views/contests.py:1230 +#: judge/views/contests.py:1225 #, python-format msgid "Contest tag: %s" msgstr "Thẻ kỳ thi %s" -#: judge/views/contests.py:1238 +#: judge/views/contests.py:1233 msgid "You are not allowed to create contests." msgstr "Bạn không có quyền tạo kỳ thi mới." -#: judge/views/contests.py:1246 judge/views/contests.py:1249 +#: judge/views/contests.py:1241 judge/views/contests.py:1244 #: templates/organization/tabs.html:32 msgid "Create new contest" msgstr "Tạo kỳ thi mới" -#: judge/views/contests.py:1314 +#: judge/views/contests.py:1309 #, python-brace-format msgid "Editing contest {0}" msgstr "Sửa kỳ thi {0}" -#: judge/views/contests.py:1317 +#: judge/views/contests.py:1312 #, python-format msgid "Editing contest %s" msgstr "Sửa kỳ thi %s" -#: judge/views/contests.py:1369 +#: judge/views/contests.py:1364 msgid "Please wait until the contest has ended to download data." msgstr "Vui lòng đợi sau khi kỳ thi kết thúc để tải dữ liệu." -#: judge/views/contests.py:1374 +#: judge/views/contests.py:1369 msgid "Download contest data" msgstr "Tải dữ liệu kỳ thi" -#: judge/views/contests.py:1407 +#: judge/views/contests.py:1402 #, python-format msgid "Preparing data for %s..." msgstr "Chuẩn bị dữ liệu cho %s..." @@ -4063,125 +3952,125 @@ msgstr "Tổ chức" msgid "Members" msgstr "Các thành viên" -#: judge/views/organization.py:211 judge/views/organization.py:214 -#: judge/views/organization.py:219 +#: judge/views/organization.py:195 judge/views/organization.py:198 +#: judge/views/organization.py:203 msgid "Joining organization" msgstr "Đang tham gia tổ chức" -#: judge/views/organization.py:211 +#: judge/views/organization.py:195 msgid "You are already in the organization." msgstr "Bạn đã trong tổ chức." -#: judge/views/organization.py:214 +#: judge/views/organization.py:198 msgid "This organization is not open." msgstr "Tổ chức này không phải là mở." -#: judge/views/organization.py:232 judge/views/organization.py:234 +#: judge/views/organization.py:216 judge/views/organization.py:218 msgid "Leaving organization" msgstr "Rời khỏi tổ chức" -#: judge/views/organization.py:232 +#: judge/views/organization.py:216 #, python-format msgid "You are not in \"%s\"." msgstr "Bạn đang không ở trong \"%s\"." -#: judge/views/organization.py:234 +#: judge/views/organization.py:218 msgid "You cannot leave an organization you own." msgstr "Bạn không thể rời tổ chức của chính mình." -#: judge/views/organization.py:250 +#: judge/views/organization.py:234 #, python-format msgid "Can't request to join %s" msgstr "Không thể yêu cầu tham gia %s" -#: judge/views/organization.py:251 +#: judge/views/organization.py:235 #, python-format msgid "You already have a pending request to join %s." msgstr "" -#: judge/views/organization.py:258 +#: judge/views/organization.py:242 #, python-format msgid "Request to join %s" msgstr "Yêu cầu tham gia %s" -#: judge/views/organization.py:276 +#: judge/views/organization.py:260 msgid "Join request detail" msgstr "Chi tiết yêu cầu tham gia" -#: judge/views/organization.py:308 judge/views/organization.py:309 +#: judge/views/organization.py:292 judge/views/organization.py:293 #, python-format msgid "Managing join requests for %s" msgstr "Quản lý các yêu cầu tham gia %s" -#: judge/views/organization.py:343 +#: judge/views/organization.py:327 #, python-format msgid "Your organization can only receive %d more member." msgid_plural "Your organization can only receive %d more members." msgstr[0] "Tổ chức của bạn chỉ có thể nhận thêm %d thành viên." -#: judge/views/organization.py:345 +#: judge/views/organization.py:329 #, python-format msgid "You cannot approve %d user." msgid_plural "You cannot approve %d users." msgstr[0] "Bạn không thể chấp nhận %d thành viên." -#: judge/views/organization.py:358 +#: judge/views/organization.py:342 #, python-format msgid "Approved %d user." msgid_plural "Approved %d users." msgstr[0] "Đã chấp nhận %d thành viên." -#: judge/views/organization.py:359 +#: judge/views/organization.py:343 #, python-format msgid "Rejected %d user." msgid_plural "Rejected %d users." msgstr[0] "Đã từ chối %d thành viên." -#: judge/views/organization.py:389 templates/organization/list-tabs.html:5 +#: judge/views/organization.py:373 templates/organization/list-tabs.html:5 msgid "Create new organization" msgstr "Tạo tổ chức mới" -#: judge/views/organization.py:412 judge/views/organization.py:416 +#: judge/views/organization.py:399 judge/views/organization.py:403 msgid "Can't create organization" msgstr "Không thể tạo tổ chức" -#: judge/views/organization.py:417 +#: judge/views/organization.py:404 msgid "You are not allowed to create new organizations." msgstr "Bạn không có quyền tạo tổ chức mới." -#: judge/views/organization.py:426 +#: judge/views/organization.py:413 #, python-format msgid "Editing %s" msgstr "Đang chỉnh sửa %s" -#: judge/views/organization.py:454 judge/views/organization.py:458 -#: judge/views/organization.py:463 +#: judge/views/organization.py:435 judge/views/organization.py:439 +#: judge/views/organization.py:444 msgid "Can't kick user" msgstr "Không thể loại thành viên" -#: judge/views/organization.py:455 +#: judge/views/organization.py:436 msgid "The user you are trying to kick does not exist!" msgstr "Thành viên bạn muốn loại không tồn tại!" -#: judge/views/organization.py:459 +#: judge/views/organization.py:440 #, python-format msgid "The user you are trying to kick is not in organization: %s" msgstr "Thành viên mà bạn muốn loại không thuộc tổ chức: %s." -#: judge/views/organization.py:464 +#: judge/views/organization.py:445 #, python-format msgid "The user you are trying to kick is an admin of organization: %s." msgstr "Thành viên mà bạn muốn loại là admin của tổ chức: %s." -#: judge/views/organization.py:599 +#: judge/views/organization.py:584 msgid "Current month" msgstr "Tháng hiện tại" -#: judge/views/organization.py:603 +#: judge/views/organization.py:588 msgid "Credit usage (hour)" msgstr "Số giờ chấm bài" -#: judge/views/organization.py:609 +#: judge/views/organization.py:594 msgid "Cost (thousand vnd)" msgstr "chi phí (nghìn đồng)" @@ -4208,28 +4097,28 @@ msgstr "Không có lời giải" msgid "Could not find an editorial with the code \"%s\"." msgstr "Không thể tìm thấy lời giải của bài tập \"%s\"." -#: judge/views/problem.py:375 +#: judge/views/problem.py:312 msgid "Problem list" msgstr "Danh sách bài" -#: judge/views/problem.py:579 +#: judge/views/problem.py:516 msgid "Suggested problem list" msgstr "Danh sách các bài được đề xuất" -#: judge/views/problem.py:653 judge/views/problem.py:661 +#: judge/views/problem.py:590 judge/views/problem.py:598 #, python-format msgid "Submit to %s" msgstr "Nộp bài %s" -#: judge/views/problem.py:706 +#: judge/views/problem.py:643 msgid "You submitted too many submissions." msgstr "Bạn đã nộp bài quá nhiều lần." -#: judge/views/problem.py:710 +#: judge/views/problem.py:647 msgid "Banned from submitting" msgstr "Không thể nộp bài" -#: judge/views/problem.py:711 +#: judge/views/problem.py:648 msgid "" "You have been declared persona non grata for this problem. You are " "permanently barred from submitting to this problem." @@ -4237,86 +4126,80 @@ msgstr "" "Bạn đã được coi là cá nhân không tính điểm cho đề này. Bạn không thể nộp bài " "này." -#: judge/views/problem.py:715 +#: judge/views/problem.py:652 msgid "Too many submissions" msgstr "Quá nhiều bài nộp" -#: judge/views/problem.py:716 +#: judge/views/problem.py:653 msgid "You have exceeded the submission limit for this problem." msgstr "Bạn đã vượt quá giới hạn lần nộp của bài này." -#: judge/views/problem.py:738 +#: judge/views/problem.py:675 #, fuzzy #| msgid "No such editorial" msgid "No credit" msgstr "Không có lời giải" -#: judge/views/problem.py:740 +#: judge/views/problem.py:677 #, python-format msgid "" "The organization %s has no credit left to execute this submission. Ask the " "organization to buy more credit." msgstr "" -#: judge/views/problem.py:814 +#: judge/views/problem.py:751 msgid "You are not allowed to submit to this problem." msgstr "Bạn không có quyền nộp bài này." -#: judge/views/problem.py:835 +#: judge/views/problem.py:772 msgid "Clone Problem" msgstr "Nhân bản bài tập" -#: judge/views/problem.py:863 +#: judge/views/problem.py:800 #, python-format msgid "Cloned problem from %s" msgstr "Nhân bản từ bài %s" -#: judge/views/problem.py:886 judge/views/problem.py:889 +#: judge/views/problem.py:823 judge/views/problem.py:826 msgid "Creating new problem" msgstr "Tạo bài tập mới" -#: judge/views/problem.py:931 judge/views/problem.py:934 +#: judge/views/problem.py:868 judge/views/problem.py:871 msgid "Suggesting new problem" msgstr "Đề xuất bài tập" -#: judge/views/problem.py:953 templates/problem/suggest.html:18 +#: judge/views/problem.py:890 templates/problem/suggest.html:18 msgid "Import problem from Codeforces Polygon package" msgstr "" -#: judge/views/problem.py:1008 +#: judge/views/problem.py:945 #, fuzzy #| msgid "Full URL to the problem." msgid "Failed to import problem" msgstr "Đường dẫn tới đề bài" -#: judge/views/problem.py:1016 templates/problem/editor.html:100 +#: judge/views/problem.py:953 templates/problem/editor.html:99 msgid "Update problem from Codeforces Polygon package" msgstr "" -#: judge/views/problem.py:1040 judge/views/problem.py:1121 +#: judge/views/problem.py:977 #, python-brace-format msgid "Editing problem {0}" msgstr "Sửa đề bài {0}" -#: judge/views/problem.py:1043 +#: judge/views/problem.py:980 #, python-format msgid "Editing problem %s" msgstr "Sửa đề bài %s" -#: judge/views/problem.py:1110 +#: judge/views/problem.py:1047 msgid "Can't edit problem" msgstr "Không thể sửa bài" -#: judge/views/problem.py:1111 +#: judge/views/problem.py:1048 msgid "You are not allowed to edit this problem." msgstr "Bạn không có quyền chỉnh sửa bài tập này." -#: judge/views/problem.py:1131 -#, fuzzy -#| msgid "Edited from site" -msgid "Edited types/group from site" -msgstr "Chỉnh sửa từ trang web" - #: judge/views/problem_data.py:40 msgid "Checker arguments must be a JSON object." msgstr "Tham số trình chấm phải là một JSON" @@ -4430,12 +4313,12 @@ msgstr "Các bài giải tốt nhất cho bài %(number)s trong %(contest)s" msgid "A username must contain letters, numbers, or underscores." msgstr "Tên người dùng phải chứa chữ cái, số hoặc dấu gạch chân" -#: judge/views/register.py:30 templates/registration/registration_form.html:258 +#: judge/views/register.py:30 templates/registration/registration_form.html:162 #: templates/user/edit-profile.html:289 msgid "Full name" msgstr "Họ và tên" -#: judge/views/register.py:31 templates/registration/registration_form.html:324 +#: judge/views/register.py:31 templates/registration/registration_form.html:203 msgid "Timezone" msgstr "Múi giờ" @@ -4481,7 +4364,7 @@ msgstr "Xác thực không thành công" msgid "New Problems" msgstr "Bài mới" -#: judge/views/status.py:24 templates/submission/list.html:382 +#: judge/views/status.py:24 templates/submission/list.html:318 msgid "Status" msgstr "Trạng thái" @@ -4523,7 +4406,7 @@ msgid "Comparing submission %(first)s with %(second)s" msgstr "So sánh bài nộp %(first)s và %(second)s" #: judge/views/submission.py:351 judge/views/submission.py:352 -#: templates/problem/problem.html:223 templates/problem/problem.html:232 +#: templates/problem/problem.html:153 templates/problem/problem.html:162 msgid "All submissions" msgstr "Danh sách bài nộp" @@ -4665,53 +4548,53 @@ msgstr "không ai cả" msgid "New Ticket Message For: %s" msgstr "Tin nhắn mới cho vấn đề: %s" -#: judge/views/two_factor.py:49 +#: judge/views/two_factor.py:50 msgid "Enable Two-factor Authentication" msgstr "" -#: judge/views/two_factor.py:109 +#: judge/views/two_factor.py:110 msgid "Refresh Two-factor Authentication" msgstr "Tạo mới xác thực 2 yếu tố" -#: judge/views/two_factor.py:117 +#: judge/views/two_factor.py:118 msgid "Disable Two-factor Authentication" msgstr "" -#: judge/views/two_factor.py:166 +#: judge/views/two_factor.py:167 msgid "Invalid WebAuthn response" msgstr "" -#: judge/views/two_factor.py:169 +#: judge/views/two_factor.py:170 msgid "Invalid name" msgstr "" -#: judge/views/two_factor.py:222 +#: judge/views/two_factor.py:223 msgid "Staff may not disable 2FA" msgstr "" -#: judge/views/two_factor.py:230 +#: judge/views/two_factor.py:231 msgid "Perform Two-factor Authentication" msgstr "" -#: judge/views/user.py:104 +#: judge/views/user.py:105 msgid "No such user" msgstr "Không có người dùng" -#: judge/views/user.py:104 +#: judge/views/user.py:105 #, python-format msgid "No user handle \"%s\"." msgstr "Không có người dùng \"%s\"." -#: judge/views/user.py:108 +#: judge/views/user.py:109 msgid "My account" msgstr "Tài khoản của tôi" -#: judge/views/user.py:109 +#: judge/views/user.py:110 #, python-format msgid "User %s" msgstr "Người dùng %s" -#: judge/views/user.py:156 +#: judge/views/user.py:157 msgid "Login" msgstr "Đăng nhập" @@ -4764,7 +4647,7 @@ msgid "Updated on site" msgstr "Cập Nhật trên trang web" #: judge/views/user.py:530 templates/admin/auth/user/change_form.html:14 -#: templates/admin/auth/user/change_form.html:17 templates/base.html:304 +#: templates/admin/auth/user/change_form.html:17 templates/base.html:284 #: templates/user/user-tabs.html:32 msgid "Edit profile" msgstr "Chỉnh sửa hồ sơ" @@ -4793,176 +4676,28 @@ msgstr "Đóng góp" msgid "You have been successfully logged out." msgstr "Bạn đã đăng xuất thành công." -#: judge/views/user.py:694 -msgid "Password reset" -msgstr "Đặt lại mật khẩu" - -#: judge/views/user.py:704 -msgid "You have sent too many password reset requests. Please try again later." -msgstr "" - -#: martor/templates/martor/editor.html:5 -#, fuzzy -#| msgid "Editorial" -msgid "Editor" -msgstr "Lời giải" - -#: martor/templates/martor/editor.html:6 -#, fuzzy -#| msgid "Prev" -msgid "Preview" -msgstr "Trước" - -#: martor/templates/martor/editor.html:11 -msgid "Uploading... please wait..." -msgstr "" - -#: martor/templates/martor/editor.html:18 -#, fuzzy -#| msgid "Nothing here." -msgid "Nothing to preview" -msgstr "Không có gì ở đây." - -#: martor/templates/martor/guide.html:4 -msgid "Markdown Guide" -msgstr "" - -#: martor/templates/martor/guide.html:5 -#, python-format -msgid "" -"This site is powered by Markdown. For full documentation, click here." -msgstr "" - -#: martor/templates/martor/guide.html:9 martor/templates/martor/toolbar.html:26 -#, fuzzy -#| msgid "QR Code" -msgid "Code" -msgstr "Mã QR" - -#: martor/templates/martor/guide.html:10 -msgid "Or" -msgstr "" - -#: martor/templates/martor/guide.html:13 -msgid "... to Get" -msgstr "" - -#: martor/templates/martor/toolbar.html:4 -msgid "Bold" -msgstr "" - -#: martor/templates/martor/toolbar.html:7 -msgid "Italic" -msgstr "" - -#: martor/templates/martor/toolbar.html:10 -msgid "Horizontal Line" -msgstr "" - -#: martor/templates/martor/toolbar.html:13 -#: martor/templates/martor/toolbar.html:16 -#: martor/templates/martor/toolbar.html:17 -#: martor/templates/martor/toolbar.html:18 -#, fuzzy -#| msgid "Grading" -msgid "Heading" -msgstr "Chấm điểm" - -#: martor/templates/martor/toolbar.html:22 -msgid "Pre or Code" -msgstr "" - -#: martor/templates/martor/toolbar.html:25 -#, fuzzy -#| msgid "Prev" -msgid "Pre" -msgstr "Trước" - -#: martor/templates/martor/toolbar.html:30 -msgid "Math" -msgstr "" - -#: martor/templates/martor/toolbar.html:33 -msgid "Inline Math" -msgstr "" - -#: martor/templates/martor/toolbar.html:34 -#, fuzzy -#| msgid "Display badge" -msgid "Display Math" -msgstr "Huy hiệu hiển thị" - -#: martor/templates/martor/toolbar.html:35 -msgid "LaTeX" -msgstr "" - -#: martor/templates/martor/toolbar.html:39 -msgid "Quote" -msgstr "" - -#: martor/templates/martor/toolbar.html:42 -msgid "Unordered List" -msgstr "" - -#: martor/templates/martor/toolbar.html:45 -#, fuzzy -#| msgid "Order in contest" -msgid "Ordered List" -msgstr "Thứ tự của bài tập trong kỳ thi" - -#: martor/templates/martor/toolbar.html:49 -#, fuzzy -#| msgid "Link" -msgid "URL/Link" -msgstr "Liên kết" - -#: martor/templates/martor/toolbar.html:52 -msgid "Insert Image Link" -msgstr "" - -#: martor/templates/martor/toolbar.html:56 -#: martor/templates/martor/toolbar.html:58 -msgid "Upload an Image" -msgstr "" - -#: martor/templates/martor/toolbar.html:61 -msgid "Direct Mention a User" -msgstr "" - -#: martor/templates/martor/toolbar.html:65 -#, fuzzy -#| msgid "Full name" -msgid "Full Screen" -msgstr "Họ và tên" - -#: martor/templates/martor/toolbar.html:68 -msgid "Markdown Guide (Help)" -msgstr "" - -#: martor/views.py:48 -#, python-format -msgid "No users registered as `%(username)s` or user is unactived." -msgstr "" +#: judge/views/user.py:694 +msgid "Password reset" +msgstr "Đặt lại mật khẩu" -#: martor/views.py:54 -msgid "Validation Failed for field `username`" +#: judge/views/user.py:704 +msgid "You have sent too many password reset requests. Please try again later." msgstr "" #: templates/admin/judge/contest/change_form.html:9 msgid "Are you sure you want to rejudge ALL the submissions?" msgstr "Bạn có chắc chắn muốn chấm lại TẤT CẢ các bài nộp?" -#: templates/admin/judge/contest/change_form.html:16 +#: templates/admin/judge/contest/change_form.html:12 msgid "Are you sure you want to rescore ALL the submissions?" msgstr "Bạn có chắc chắn muốn tính điểm lại lại TẤT CẢ các bài nộp?" -#: templates/admin/judge/contest/change_form.html:19 +#: templates/admin/judge/contest/change_form.html:15 msgid "Are you sure you want to resend this announcement?" msgstr "Bạn có chắc muốn gửi lại thông báo này?" -#: templates/admin/judge/contest/change_form.html:27 -#: templates/admin/judge/contest/change_form.html:30 +#: templates/admin/judge/contest/change_form.html:23 +#: templates/admin/judge/contest/change_form.html:26 msgid "Rate" msgstr "" @@ -5006,68 +4741,78 @@ msgstr "Xem các bài nộp" msgid "Edit user" msgstr "Cập nhật người dùng" -#: templates/base.html:267 templates/contest/contest-tabs.html:21 +#: templates/base.html:247 templates/contest/contest-tabs.html:21 #: templates/contest/prepare-data.html:97 templates/organization/tabs.html:19 -#: templates/problem/unified_view.html:13 templates/user/prepare-data.html:104 +#: templates/user/prepare-data.html:104 msgid "Submissions" msgstr "Các bài nộp" -#: templates/base.html:269 templates/contest/list.html:200 +#: templates/base.html:249 templates/contest/list.html:200 #: templates/contest/list.html:240 templates/contest/list.html:333 #: templates/organization/tabs.html:16 msgid "Users" msgstr "Thành viên" -#: templates/base.html:281 +#: templates/base.html:261 msgid "Report issue" msgstr "Báo cáo vấn đề" -#: templates/base.html:297 +#: templates/base.html:277 #, python-format msgid "Hello, %(username)s." msgstr "Xin chào, %(username)s." -#: templates/base.html:306 +#: templates/base.html:286 msgid "Stop impersonating" msgstr "Ngừng mạo danh" -#: templates/base.html:311 +#: templates/base.html:291 msgid "Log out" msgstr "Đăng xuất" -#: templates/base.html:320 +#: templates/base.html:300 #: templates/registration/password_reset_complete.html:4 msgid "Log in" msgstr "Đăng nhập" -#: templates/base.html:322 templates/registration/registration_form.html:329 +#: templates/base.html:301 templates/registration/registration_form.html:207 msgid "or" msgstr "hoặc" -#: templates/base.html:323 +#: templates/base.html:302 msgid "Sign up" msgstr "Đăng ký" -#: templates/base.html:337 +#: templates/base.html:315 msgid "spectating" msgstr "spectating" -#: templates/base.html:348 +#: templates/base.html:326 msgid "Go to Rankings" msgstr "Tới bảng xếp hạng" -#: templates/base.html:355 +#: templates/base.html:333 msgid "This site works best with JavaScript enabled." msgstr "Trang web này hoạt động tốt nhất khi JavaScript được cho phép." -#: templates/base.html:385 +#: templates/base.html:384 +#, python-format msgid "proudly powered by" msgstr "dựa trên" -#: templates/base.html:386 +#: templates/base.html:362 +msgid "System Status" +msgstr "Trạng thái hệ thống" + +#: templates/base.html:364 #, python-format -msgid "%(github)s" -msgstr "" +msgid "follow us on %(github)s and %(facebook)s" +msgstr "theo dõi LCOJ trên %(github)s và %(facebook)s" + +#: templates/base.html:363 +#, python-format +msgid "made with love by %(facebook)s" +msgstr "được tạo bởi %(facebook)s" #: templates/blog/blog-list-tabs.html:4 msgid "Newsfeed" @@ -5079,8 +4824,7 @@ msgstr "Blog" #: templates/blog/blog-post.html:10 templates/blog/blog-post.html:19 #: templates/blog/content.html:22 templates/blog/content.html:31 -#: templates/comments/list.html:38 templates/comments/list.html:39 -#: templates/comments/list.html:47 templates/comments/list.html:48 +#: templates/comments/list.html:36 templates/comments/list.html:45 #: templates/user/comment.html:42 templates/user/comment.html:51 msgid "Please log in to vote" msgstr "Hãy đăng nhập để bình chọn" @@ -5099,8 +4843,8 @@ msgstr "Đọc tiếp..." msgid "o{time}" msgstr "o{time}" -#: templates/blog/content.html:49 templates/comments/list.html:96 -#: templates/comments/list.html:111 templates/contest/contest-tabs.html:27 +#: templates/blog/content.html:49 templates/comments/list.html:94 +#: templates/comments/list.html:110 templates/contest/contest-tabs.html:27 #: templates/contest/tag-title.html:9 templates/flatpages/admin_link.html:3 #: templates/license.html:10 templates/problem/editorial.html:14 msgid "Edit" @@ -5117,89 +4861,71 @@ msgstr "đã đăng vào %(time)s" msgid "Are you sure you want to delete this blog post?" msgstr "Bạn có chắc muốn xoá khoá bảo mật này?" -#: templates/blog/edit.html:38 +#: templates/blog/edit.html:37 msgid "Edit blog post in admin panel for more options" msgstr "Sửa blog này ở admin panel để có nhiều tùy chỉnh hơn" -#: templates/blog/edit.html:42 +#: templates/blog/edit.html:41 msgid "Please read the [guidelines][0] before creating a new blog post." msgstr "Hãy đọc [nội quy blog][0] trước khi tạo blog mới." -#: templates/blog/edit.html:55 templates/contest/edit.html:128 -#: templates/problem/editor.html:122 templates/user/edit-profile.html:437 +#: templates/blog/edit.html:54 templates/contest/edit.html:127 +#: templates/problem/editor.html:121 templates/user/edit-profile.html:437 msgid "Delete" msgstr "Xoá" -#: templates/blog/edit.html:56 templates/contest/edit.html:139 -#: templates/organization/edit.html:29 +#: templates/blog/edit.html:55 templates/contest/edit.html:138 +#: templates/organization/edit.html:26 #: templates/organization/requests/pending.html:53 -#: templates/problem/editor.html:138 -#: templates/problem/type-group-editor.html:21 -#: templates/ticket/edit-notes.html:4 +#: templates/problem/editor.html:137 templates/ticket/edit-notes.html:4 msgid "Update" msgstr "Cập nhật" -#: templates/blog/edit.html:56 templates/contest/edit.html:141 -#: templates/organization/edit.html:27 templates/organization/new.html:10 +#: templates/blog/edit.html:55 templates/contest/edit.html:140 +#: templates/organization/edit.html:24 templates/organization/new.html:10 #: templates/problem/suggest.html:25 templates/tag/create.html:27 msgid "Create" msgstr "Tạo" -#: templates/blog/list.html:52 +#: templates/blog/list.html:48 msgid "Blog" msgstr "Blog" -#: templates/blog/list.html:54 +#: templates/blog/list.html:50 msgid "Events" msgstr "Sự kiện" -#: templates/blog/list.html:64 -#, fuzzy -#| msgid "Search problems..." -msgid "Search blog" -msgstr "Tìm bài..." - -#: templates/blog/list.html:65 -#, fuzzy -#| msgid "Sign up with Google" -msgid "Search with Google" -msgstr "Đăng ký bằng Gmail" - -#: templates/blog/list.html:66 -msgid "Search" -msgstr "" - -#: templates/blog/list.html:83 +#: templates/blog/list.html:69 msgid "My open tickets" msgstr "Báo cáo của tôi" -#: templates/blog/list.html:104 +#: templates/blog/list.html:90 msgid "New tickets" msgstr "Báo cáo mới" -#: templates/blog/list.html:125 templates/contest/list.html:235 +#: templates/blog/list.html:111 templates/contest/list.html:235 msgid "Ongoing contests" msgstr "Các kỳ thi đang diễn ra" -#: templates/blog/list.html:133 +#: templates/blog/list.html:119 #, python-format msgid "Ends in %(countdown)s." msgstr "Kết thúc trong %(countdown)s." -#: templates/blog/list.html:143 templates/contest/list.html:274 +#: templates/blog/list.html:129 templates/contest/list.html:274 msgid "Upcoming contests" msgstr "Các kỳ thi sắp tới" -#: templates/blog/list.html:151 templates/contest/contest.html:55 +#: templates/blog/list.html:137 templates/contest/contest.html:55 #, python-format msgid "Starting in %(countdown)s." msgstr "Bắt đầu trong %(countdown)s." -#: templates/blog/list.html:168 +#: templates/blog/list.html:154 msgid "Comment stream" msgstr "Dòng bình luận" -#: templates/blog/list.html:197 templates/organization/home.html:164 +#: templates/blog/list.html:183 templates/organization/home.html:164 msgid "New problems" msgstr "Bài mới" @@ -5232,7 +4958,7 @@ msgstr "chỉnh sửa {edits}" msgid "original" msgstr "" -#: templates/comments/base-media-js.html:91 templates/comments/list.html:80 +#: templates/comments/base-media-js.html:91 templates/comments/list.html:78 #: templates/user/comment.html:88 msgid "edited" msgstr "chỉnh sửa" @@ -5263,83 +4989,78 @@ msgstr "Không thể cập nhật bình luận." msgid "Could not edit comment: {error}" msgstr "Không thể chỉnh sửa bình luận: {error}" -#: templates/comments/base-media-js.html:244 -#: templates/comments/base-media-js.html:263 -msgid "Show more" -msgstr "" - -#: templates/comments/base-media-js.html:260 -msgid "Show less" -msgstr "" - -#: templates/comments/base-media-js.html:350 +#: templates/comments/base-media-js.html:221 msgid "" -"Looks like you're trying to post some source code!\n" +"Looks like you're trying to post a source code!\n" "\n" -"The comment section is not for posting source code.\n" +"The comment section are not for posting source code.\n" "If you want to submit your solution, please use the \"Submit solution\" " "button.\n" "\n" "Are you sure you want to post this?" msgstr "" -#: templates/comments/edit-ajax.html:16 templates/comments/list.html:169 +#: templates/comments/edit-ajax.html:16 templates/comments/list.html:168 #: templates/ticket/ticket.html:206 msgid "Post!" msgstr "Đăng!" -#: templates/comments/list.html:3 templates/problem/unified_view.html:17 -#: templates/user/prepare-data.html:98 templates/user/user-tabs.html:13 +#: templates/comments/list.html:3 templates/user/prepare-data.html:98 +#: templates/user/user-tabs.html:13 msgid "Comments" msgstr "Bình luận" -#: templates/comments/list.html:12 +#: templates/comments/list.html:6 +msgid "Please read the [guidelines][0] before commenting." +msgstr "Hãy đọc [nội quy][0] trước khi bình luận." + +#: templates/comments/list.html:11 msgid "Comments are disabled on this page." msgstr "Bình luận đã bị vô hiệu hóa trên trang này." -#: templates/comments/list.html:17 +#: templates/comments/list.html:16 msgid "There are no comments at the moment." msgstr "Không có bình luận tại thời điểm này." -#: templates/comments/list.html:67 templates/user/comment.html:71 +#: templates/comments/list.html:65 templates/user/comment.html:71 #, python-brace-format msgid "commented on {time}" msgstr "đã bình luận lúc {time}" -#: templates/comments/list.html:78 templates/user/comment.html:86 +#: templates/comments/list.html:76 templates/user/comment.html:86 #, python-format msgid "edit %(edits)s" msgstr "sửa %(edits)s" -#: templates/comments/list.html:89 templates/user/comment.html:97 +#: templates/comments/list.html:87 templates/user/comment.html:97 msgid "Link" msgstr "Liên kết" -#: templates/comments/list.html:93 templates/comments/list.html:106 +#: templates/comments/list.html:92 templates/comments/list.html:106 msgid "Reply" msgstr "Phản hồi" -#: templates/comments/list.html:99 templates/comments/votes.html:1 +#: templates/comments/list.html:98 templates/comments/votes.html:1 msgid "Votes" msgstr "" -#: templates/comments/list.html:100 +#: templates/comments/list.html:99 msgid "Hide" msgstr "Ẩn" -#: templates/comments/list.html:125 templates/user/comment.html:109 +#: templates/comments/list.html:124 templates/user/comment.html:109 msgid "This comment is hidden due to too much negative feedback." msgstr "Bình luận này đã bị ẩn vì có quá nhiều phản ứng tiêu cực." -#: templates/comments/list.html:126 templates/user/comment.html:110 +#: templates/comments/list.html:125 templates/user/comment.html:110 msgid "Show it anyway." msgstr "Nhấn để xem." -#: templates/comments/list.html:147 +#: templates/comments/list.html:146 msgid "New comment" msgstr "Bình luận mới" -#: templates/comments/list.html:161 +#: templates/comments/list.html:160 msgid "Invalid comment body." msgstr "Bình luận không hợp lệ." @@ -5416,23 +5137,21 @@ msgid "Clone!" msgstr "Nhân bản!" #: templates/contest/contest-all-problems.html:40 -#: templates/problem/submit_partial.html:200 -#: templates/problem/submit_partial.html:204 msgid "Submit" msgstr "Nộp bài" #: templates/contest/contest-all-problems.html:45 -#: templates/problem/problem.html:288 templates/problem/raw.html:62 +#: templates/problem/problem.html:214 templates/problem/raw.html:62 msgid "Time limit:" msgstr "Giới hạn thời gian:" #: templates/contest/contest-all-problems.html:46 -#: templates/problem/problem.html:300 templates/problem/raw.html:71 +#: templates/problem/problem.html:226 templates/problem/raw.html:71 msgid "Memory limit:" msgstr "Giới hạn bộ nhớ:" #: templates/contest/contest-all-problems.html:47 -#: templates/problem/problem.html:278 +#: templates/problem/problem.html:204 msgid "Points:" msgstr "Điểm:" @@ -5475,7 +5194,7 @@ msgstr "Lịch" msgid "Info" msgstr "Thông tin" -#: templates/contest/contest-tabs.html:6 templates/submission/list.html:418 +#: templates/contest/contest-tabs.html:6 templates/submission/list.html:354 #: templates/user/user-tabs.html:8 msgid "Statistics" msgstr "Thống kê" @@ -5719,9 +5438,8 @@ msgstr "Tải dữ liệu" msgid "Editorials" msgstr "Lời giải" -#: templates/contest/contest.html:257 templates/problem/editor.html:133 -#: templates/problem/list.html:255 templates/problem/unified_view.html:9 -#: templates/problem/unified_view.html:122 +#: templates/contest/contest.html:257 templates/problem/editor.html:132 +#: templates/problem/list.html:181 msgid "Editorial" msgstr "Lời giải" @@ -5745,35 +5463,35 @@ msgstr "Tiêu đề" msgid "Add an announcement" msgstr "Tạo thông báo" -#: templates/contest/contest.html:305 templates/problem/problem.html:454 +#: templates/contest/contest.html:305 templates/problem/problem.html:380 msgid "Clarifications" msgstr "Làm rõ" -#: templates/contest/create-announcement.html:19 +#: templates/contest/create-announcement.html:18 msgid "Announce" msgstr "Thông báo" -#: templates/contest/edit.html:31 templates/problem/editor.html:25 +#: templates/contest/edit.html:30 templates/problem/editor.html:24 msgid "Press Enter to select multiple users..." msgstr "Nhấn Enter để chọn nhiều thành viên..." -#: templates/contest/edit.html:100 +#: templates/contest/edit.html:99 msgid "Edit contest in admin panel for more options" msgstr "Sửa contest này ở admin panel để có nhiều tùy chỉnh hơn" -#: templates/contest/edit.html:118 +#: templates/contest/edit.html:117 msgid "Order in contest" msgstr "Thứ tự của bài tập trong kỳ thi" -#: templates/contest/edit.html:120 +#: templates/contest/edit.html:119 msgid "Max submission" msgstr "Số lượng submission" -#: templates/contest/edit.html:123 +#: templates/contest/edit.html:122 msgid "Maximum number of submissions" msgstr "Số lần nộp bài" -#: templates/contest/edit.html:125 +#: templates/contest/edit.html:124 msgid "or leave blank for no limit." msgstr "hoặc để trống để không giới hạn." @@ -6129,42 +5847,6 @@ msgstr "Bài nộp theo ngôn ngữ" msgid "Language AC Rate" msgstr "Tỉ lệ AC theo ngôn ngữ" -#: templates/donate-button.html:10 -msgid "Support Luyen Code Online" -msgstr "Ủng hộ Luyện Code Online" - -#: templates/donate-button.html:13 -msgid "Thank you for your interest in supporting us!" -msgstr "Cảm ơn bạn đã quan tâm ủng hộ chúng tôi!" - -#: templates/donate-button.html:14 -msgid "Your donation will be used to:" -msgstr "Khoản ủng hộ của bạn sẽ được sử dụng để:" - -#: templates/donate-button.html:16 -msgid "Maintain and upgrade servers" -msgstr "Duy trì và nâng cấp máy chủ" - -#: templates/donate-button.html:17 -msgid "Expand problem sets and learning materials" -msgstr "Mở rộng bộ đề bài và tài liệu học tập" - -#: templates/donate-button.html:18 -msgid "Improve user experience" -msgstr "Cải thiện trải nghiệm người dùng" - -#: templates/donate-button.html:23 -msgid "QR Code" -msgstr "Mã QR" - -#: templates/donate-button.html:24 -msgid "Scan QR code to transfer" -msgstr "Quét mã QR để chuyển khoản" - -#: templates/donate-button.html:28 -msgid "Thank you so much for your support!" -msgstr "Cảm ơn bạn rất nhiều vì sự ủng hộ!" - #: templates/leave-warning.html:6 msgid "Changes you made may not be saved." msgstr "Những thay đổi của bạn có thể không được lưu lại." @@ -6300,7 +5982,7 @@ msgstr "Tham gia tổ chức" msgid "Request membership" msgstr "Yêu cầu tư cách thành viên" -#: templates/organization/home.html:109 +#: templates/organization/home.html:108 msgid "Organization cost" msgstr "Chi phí sử dụng tổ chức" @@ -6383,7 +6065,7 @@ msgid "There are no requests to approve." msgstr "Không có yêu cầu để chấp nhận." #: templates/organization/requests/pending.html:36 -#: templates/problem/data.html:731 +#: templates/problem/data.html:728 msgid "Delete?" msgstr "Xoá?" @@ -6444,7 +6126,7 @@ msgstr "Số giờ chấm bài hàng tháng" msgid "Kick" msgstr "Loại" -#: templates/organization/users.html:33 +#: templates/organization/users.html:31 msgid "Are you sure you want to kick this user?" msgstr "Bạn có chắc muốn loại người dùng này?" @@ -6524,85 +6206,84 @@ msgid "" "saved!" msgstr "Do đó, chỉ ${window.testcase_limit} các test đầu tiên sẽ được lưu lại!" -#: templates/problem/data.html:577 +#: templates/problem/data.html:574 msgid "Test file must be a ZIP file" msgstr "Tập tin dữ liệu phải có định dạng ZIP" -#: templates/problem/data.html:691 +#: templates/problem/data.html:688 msgid "View YAML" msgstr "Xem YAML" -#: templates/problem/data.html:710 +#: templates/problem/data.html:707 msgid "Test cases have been filled automatically!" msgstr "Các test đã được điền tự động!" -#: templates/problem/data.html:712 +#: templates/problem/data.html:709 msgid "Test cases have been filled automatically and **not saved yet**!" msgstr "Các test dưới đây được điền tự động và **chưa được lưu lại**!" -#: templates/problem/data.html:714 +#: templates/problem/data.html:711 msgid "" "Please modify the table below if needed and press the `Apply` button to save!" msgstr "Hãy chỉnh sửa bảng dưới đây nếu cần thiết, và nhấn nút `Apply` để lưu!" -#: templates/problem/data.html:724 +#: templates/problem/data.html:721 msgid "Type" msgstr "Kiểu" -#: templates/problem/data.html:725 +#: templates/problem/data.html:722 msgid "Input file" msgstr "Tập tin đầu vào" -#: templates/problem/data.html:726 +#: templates/problem/data.html:723 msgid "Output file" msgstr "Tập tin đầu ra" -#: templates/problem/data.html:728 +#: templates/problem/data.html:725 msgid "Pretest?" msgstr "" -#: templates/problem/data.html:729 +#: templates/problem/data.html:726 msgid "Generator args" msgstr "Tham số trình sinh tests" -#: templates/problem/data.html:766 +#: templates/problem/data.html:763 msgid "Edit generator args" msgstr "Sửa tham số trình sinh test" -#: templates/problem/data.html:776 +#: templates/problem/data.html:773 msgid "Apply!" msgstr "" -#: templates/problem/data.html:777 +#: templates/problem/data.html:774 msgid "Add new case" msgstr "Thêm test mới" -#: templates/problem/data.html:779 +#: templates/problem/data.html:776 msgid "Save" msgstr "Lưu" -#: templates/problem/editor.html:94 +#: templates/problem/editor.html:93 msgid "Edit problem in admin panel for more options" msgstr "Sửa bài tập này ở admin panel để có nhiều tùy chỉnh hơn" -#: templates/problem/editor.html:105 -#: templates/problem/type-group-editor.html:14 +#: templates/problem/editor.html:104 msgid "Please correct the error below." msgstr "Xin hãy sửa các lỗi sai dưới đây." -#: templates/problem/editor.html:113 +#: templates/problem/editor.html:112 msgid "Language-specific resource limit" msgstr "Giới hạn theo ngôn ngữ" -#: templates/problem/editor.html:114 +#: templates/problem/editor.html:113 msgid "Only use this feature if you really need to!" msgstr "Chỉ sử dụng tính năng này nếu thật sự cần thiết!" -#: templates/problem/editor.html:120 +#: templates/problem/editor.html:119 msgid "Time limit (seconds)" msgstr "Giới hạn thời gian (giây):" -#: templates/problem/editor.html:121 +#: templates/problem/editor.html:120 msgid "Memory limit (KiB)" msgstr "Giới hạn bộ nhớ (KiB):" @@ -6621,7 +6302,7 @@ msgid "" msgstr "" "Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban." -#: templates/problem/editorial.html:30 templates/problem/problem.html:339 +#: templates/problem/editorial.html:30 templates/problem/problem.html:265 msgid "Author:" msgid_plural "Authors:" msgstr[0] "Tác giả:" @@ -6645,54 +6326,34 @@ msgstr "" msgid "Import" msgstr "" -#: templates/problem/list.html:79 templates/submission/list.html:71 -#, fuzzy -#| msgid "Blog post not found." -msgid "No blog posts found" -msgstr "Không tìm thấy blog." - -#: templates/problem/list.html:84 templates/submission/list.html:76 -msgid "Failed to load blog posts" -msgstr "" - -#: templates/problem/list.html:115 +#: templates/problem/list.html:50 msgid "Filter by type..." msgstr "Lọc theo dạng..." -#: templates/problem/list.html:200 +#: templates/problem/list.html:135 msgid "Hot problems" msgstr "Những bài tập nổi bật" -#: templates/problem/list.html:212 templates/submission/list.html:430 -#, fuzzy -#| msgid "blog posts" -msgid "Blog Posts" -msgstr "Bài đăng blog" - -#: templates/problem/list.html:215 templates/submission/list.html:433 -msgid "Loading..." -msgstr "Đang tải..." - -#: templates/problem/list.html:233 templates/problem/submission-diff.html:111 +#: templates/problem/list.html:159 templates/problem/submission-diff.html:111 #: templates/status/language-list.html:34 templates/ticket/list.html:198 msgid "ID" msgstr "ID" -#: templates/problem/list.html:239 templates/problem/search-form.html:35 +#: templates/problem/list.html:165 templates/problem/search-form.html:35 #: templates/user/user-problems.html:59 msgid "Category" msgstr "Nhóm" -#: templates/problem/list.html:243 +#: templates/problem/list.html:169 msgid "Types" msgstr "Dạng" -#: templates/problem/list.html:250 +#: templates/problem/list.html:176 #, python-format msgid "%% AC" msgstr "" -#: templates/problem/list.html:253 +#: templates/problem/list.html:179 msgid "# AC" msgstr "" @@ -6804,113 +6465,109 @@ msgstr "Đề bài" msgid "Suggest" msgstr "Đề xuất" -#: templates/problem/problem.html:187 +#: templates/problem/problem.html:120 msgid "View as PDF" msgstr "Xem dạng PDF" -#: templates/problem/problem.html:198 templates/problem/problem.html:208 -#: templates/problem/problem.html:213 +#: templates/problem/problem.html:129 templates/problem/problem.html:139 +#: templates/problem/problem.html:144 msgid "Submit solution" msgstr "Gửi bài giải" -#: templates/problem/problem.html:201 +#: templates/problem/problem.html:132 #, python-format msgid "%(counter)s submission left" msgid_plural "%(counter)s submissions left" msgstr[0] "Còn %(counter)s lần nộp" msgstr[1] "Còn %(counter)s lần nộp" -#: templates/problem/problem.html:209 +#: templates/problem/problem.html:140 msgid "0 submissions left" msgstr "Còn 0 lần nộp" -#: templates/problem/problem.html:221 templates/problem/problem.html:229 +#: templates/problem/problem.html:151 templates/problem/problem.html:159 msgid "My submissions" msgstr "Bài nộp của tôi" -#: templates/problem/problem.html:224 templates/problem/problem.html:233 +#: templates/problem/problem.html:154 templates/problem/problem.html:163 msgid "Best submissions" msgstr "Bài nộp tốt nhất" -#: templates/problem/problem.html:237 +#: templates/problem/problem.html:167 msgid "Read editorial" msgstr "Đọc lời giải" -#: templates/problem/problem.html:242 +#: templates/problem/problem.html:172 msgid "Manage tickets" msgstr "Quản lý báo cáo" -#: templates/problem/problem.html:246 +#: templates/problem/problem.html:176 msgid "Edit problem" msgstr "Sửa đề bài" -#: templates/problem/problem.html:248 +#: templates/problem/problem.html:178 msgid "Edit test data" msgstr "Sửa đổi test" -#: templates/problem/problem.html:253 +#: templates/problem/problem.html:183 msgid "My tickets" msgstr "Các báo cáo của tôi" -#: templates/problem/problem.html:260 -msgid "Edit type group" -msgstr "" - -#: templates/problem/problem.html:265 +#: templates/problem/problem.html:191 msgid "Manage submissions" msgstr "Quản lí submissions" -#: templates/problem/problem.html:281 templates/problem/problem.html:283 +#: templates/problem/problem.html:207 templates/problem/problem.html:209 msgid "(partial)" msgstr "(OI)" -#: templates/problem/problem.html:313 templates/problem/problem.html:322 +#: templates/problem/problem.html:239 templates/problem/problem.html:248 #: templates/submission/status-testcases.html:135 msgid "Input:" msgstr "" -#: templates/problem/problem.html:317 templates/problem/problem.html:326 +#: templates/problem/problem.html:243 templates/problem/problem.html:252 msgid "Output:" msgstr "" -#: templates/problem/problem.html:350 +#: templates/problem/problem.html:276 msgid "Suggester:" msgstr "Người đăng:" -#: templates/problem/problem.html:361 +#: templates/problem/problem.html:287 msgid "Problem source:" msgstr "Nguồn bài:" -#: templates/problem/problem.html:370 templates/problem/unified_view.html:97 +#: templates/problem/problem.html:296 msgid "Problem type" msgid_plural "Problem types" msgstr[0] "Dạng bài" msgstr[1] "Dạng bài" -#: templates/problem/problem.html:383 templates/problem/unified_view.html:68 +#: templates/problem/problem.html:309 msgid "Allowed languages" msgstr "Ngôn ngữ cho phép" -#: templates/problem/problem.html:391 templates/problem/unified_view.html:77 +#: templates/problem/problem.html:317 #, python-format msgid "No %(lang)s judge online" msgstr "Không có máy chấm %(lang)s đang online" -#: templates/problem/problem.html:402 +#: templates/problem/problem.html:328 msgid "Judge:" msgid_plural "Judges:" msgstr[0] "Máy chấm" msgstr[1] "Các máy chấm" -#: templates/problem/problem.html:419 +#: templates/problem/problem.html:345 msgid "none available" msgstr "Không có máy chấm nào" -#: templates/problem/problem.html:431 +#: templates/problem/problem.html:357 msgid "This problem is only a suggestion!" msgstr "Bài tập này đang được đề xuất!" -#: templates/problem/problem.html:433 +#: templates/problem/problem.html:359 msgid "" "This problem cannot be accessed by normal users until it is approved by an " "admin or staff. Please make sure that all the data for this problem is " @@ -6919,15 +6576,15 @@ msgstr "" "Người dùng chưa thể truy cập bài tập này đến khi nó được thông qua bởi " "admin. Hãy đảm bảo các dữ liệu của đề bài này là chính xác." -#: templates/problem/problem.html:443 +#: templates/problem/problem.html:369 msgid "Request clarification" msgstr "Gửi thắc mắc" -#: templates/problem/problem.html:445 +#: templates/problem/problem.html:371 msgid "Report an issue" msgstr "Báo cáo vấn đề" -#: templates/problem/problem.html:465 +#: templates/problem/problem.html:391 msgid "No clarifications have been made at this time." msgstr "Chưa có làm rõ nào được đưa ra ở thời điểm này." @@ -6956,7 +6613,7 @@ msgid "Show problem types" msgstr "Hiện dạng bài" #: templates/problem/search-form.html:38 templates/problem/search-form.html:40 -#: templates/submission/list.html:403 +#: templates/submission/list.html:339 #: templates/submission/submission-list-tabs.html:4 msgid "All" msgstr "Tất cả" @@ -6969,7 +6626,7 @@ msgstr "Dạng bài" msgid "Point range" msgstr "Khoảng điểm" -#: templates/problem/search-form.html:66 templates/submission/list.html:413 +#: templates/problem/search-form.html:66 templates/submission/list.html:349 #: templates/tag/search-form.html:21 templates/ticket/list.html:187 msgid "Go" msgstr "Tìm" @@ -6998,16 +6655,15 @@ msgstr "Ngày" msgid "Diff source code" msgstr "So sánh mã nguồn" -#: templates/problem/submit.html:139 templates/problem/submit_js.html:189 +#: templates/problem/submit.html:139 msgid "Your source code must contain at most 65536 characters." msgstr "Độ dài code không được vượt quá 65536 ký tự." -#: templates/problem/submit.html:161 templates/problem/submit_js.html:211 -#: templates/problem/submit_partial.html:227 +#: templates/problem/submit.html:161 msgid "Click to select a file or drag a file into this box" msgstr "Nhấn để chọn file hoặc kéo thả file vào đây" -#: templates/problem/submit.html:162 templates/problem/submit_js.html:212 +#: templates/problem/submit.html:162 #, python-brace-format msgid "" "`Only accept \"${file_ext}\". Maximum file size is ${file_size_limit}MB.`" @@ -7015,32 +6671,32 @@ msgstr "" "`Chỉ chấp nhận file có đuôi \"${file_ext}\". Độ lớn tối đa của file là " "${file_size_limit}MB.`" -#: templates/problem/submit.html:228 templates/problem/submit_js.html:278 +#: templates/problem/submit.html:228 msgid "File name" msgstr "Tên file" -#: templates/problem/submit.html:229 templates/problem/submit_js.html:279 +#: templates/problem/submit.html:229 msgid "File size" msgstr "Độ lớn của file" -#: templates/problem/submit.html:252 templates/problem/submit_partial.html:4 +#: templates/problem/submit.html:252 msgid "Warning!" msgstr "" -#: templates/problem/submit.html:253 templates/problem/submit_partial.html:5 +#: templates/problem/submit.html:253 #, python-format msgid "" "Your default language, %(language)s, is unavailable for this problem and has " "been deselected." msgstr "Ngôn ngữ mặc định của bạn, %(language)s, không chấm được ở bài này." -#: templates/problem/submit.html:261 templates/problem/submit_partial.html:12 +#: templates/problem/submit.html:261 #, python-format msgid "You have %(left)s submission left" msgid_plural "You have %(left)s submissions left" msgstr[0] "Bạn còn %(left)s lần nộp" -#: templates/problem/submit.html:266 templates/problem/submit_partial.html:16 +#: templates/problem/submit.html:266 msgid "You have 0 submissions left" msgstr "Bạn còn 0 lần nộp" @@ -7052,7 +6708,7 @@ msgstr "Dán bài làm của bạn ở đây hoặc nhập từ file:" msgid "You can only submit file for this language." msgstr "Ngôn ngữ này chỉ chấp nhận nộp bằng file." -#: templates/problem/submit.html:315 templates/problem/submit_partial.html:215 +#: templates/problem/submit.html:315 msgid "No judge is available for this problem." msgstr "Bài tập này hiện tại không chấm." @@ -7060,21 +6716,6 @@ msgstr "Bài tập này hiện tại không chấm." msgid "Submit!" msgstr "Nộp bài!" -#: templates/problem/submit_partial.html:179 -#, fuzzy -#| msgid "Input from file" -msgid "Load from file" -msgstr "Tập tin đầu vào" - -#: templates/problem/submit_partial.html:187 -#: templates/problem/unified_view.html:20 -msgid "Toggle Full Width" -msgstr "" - -#: templates/problem/submit_partial.html:190 -msgid "Full Screen (Esc to exit)" -msgstr "" - #: templates/problem/suggest.html:8 msgid "Thanks for suggesting problem!" msgstr "Cảm ơn bạn đã đề xuất!" @@ -7092,82 +6733,6 @@ msgid "If you don't know how to suggest a problem, please read [guide][0]." msgstr "" "Nếu bạn không biết cách đề xuất bài tập mới, hãy tham khảo [hướng dẫn][0]." -#: templates/problem/unified_view.html:34 -#, fuzzy -#| msgid "Time limit:" -msgid "Time Limit" -msgstr "Giới hạn thời gian:" - -#: templates/problem/unified_view.html:37 -#, fuzzy -#| msgid "Memory limit:" -msgid "Memory Limit" -msgstr "Giới hạn bộ nhớ:" - -#: templates/problem/unified_view.html:40 -#, fuzzy -#| msgid "Standard Input/Output" -msgid "Input/Output" -msgstr "Đầu vào/ra chuẩn" - -#: templates/problem/unified_view.html:87 -#, fuzzy -#| msgid "source" -msgid "Source" -msgstr "mã nguồn" - -#: templates/problem/unified_view.html:110 -#, fuzzy -#| msgid "Author:" -#| msgid_plural "Authors:" -msgid "Author" -msgstr "Tác giả:" - -#: templates/problem/unified_view.html:131 -msgid "Warning: Spoilers Ahead" -msgstr "Cảnh báo: Nội dung tiết lộ" - -#: templates/problem/unified_view.html:132 -msgid "" -"This editorial contains hints and solutions. Are you sure you want to see it?" -msgstr "Bài viết này chứa gợi ý và lời giải. Bạn có chắc chắn muốn xem không?" - -#: templates/problem/unified_view.html:138 -msgid "Reveal Editorial" -msgstr "Hiện lời giải" - -#: templates/problem/unified_view.html:149 -msgid "My Submissions" -msgstr "Bài nộp của tôi" - -#: templates/problem/unified_view.html:162 -msgid "View All Submissions" -msgstr "Xem tất cả bài nộp" - -#: templates/problem/unified_view.html:166 -msgid "No submissions yet." -msgstr "Chưa có bài nộp nào." - -#: templates/problem/unified_view.html:169 -msgid "Please log in to view submissions." -msgstr "Vui lòng đăng nhập để xem bài nộp." - -#: templates/problem/unified_view.html:186 -msgid "Warning: Community Discussion" -msgstr "Cảnh báo: Thảo luận cộng đồng" - -#: templates/problem/unified_view.html:187 -msgid "" -"Comments may contain user-submitted hints and solutions. Proceed with " -"caution." -msgstr "" -"Bình luận có thể chứa gợi ý và lời giải từ người dùng. Hãy cân nhắc trước " -"khi xem." - -#: templates/problem/unified_view.html:193 -msgid "View Comments" -msgstr "Xem bình luận" - #: templates/registration/activate.html:3 #, python-format msgid "%(key)s is an invalid activation key." @@ -7177,45 +6742,12 @@ msgstr "%(key)s là một mã kích hoạt không hợp lệ." msgid "Your account has been successfully activated." msgstr "Tài khoản của bạn đã được kích hoạt thành công." -#: templates/registration/activation_email.html:6 +#: templates/registration/activation_email.html:1 #, python-format msgid "Thanks for registering on the %(site_name)s! We're glad to have you." msgstr "Cảm ơn bạn đã đăng ký tài khoản ở trang %(site_name)s!" -#: templates/registration/activation_email.html:8 -#, python-format -msgid "" -"The last step is activating your account. Please activate your %(site_name)s " -"account in the next %(count)s day." -msgid_plural "" -"The last step is activating your account. Please activate your %(site_name)s " -"account in the next %(count)s days." -msgstr[0] "" -"Bước cuối cùng là kích hoạt tài khoản của bạn. Vui lòng kích hoạt tài khoản " -"%(site_name)s của bạn trong vòng %(count)s ngày." -msgstr[1] "" -"Bước cuối cùng là kích hoạt tài khoản của bạn. Vui lòng kích hoạt tài khoản " -"%(site_name)s của bạn trong vòng %(count)s ngày." - -#: templates/registration/activation_email.html:14 -#: templates/registration/activation_email.txt:3 -msgid "Please click on the following link to activate your account:" -msgstr "Hãy nhấn vào link này để kích hoạt tài khoản:" - -#: templates/registration/activation_email.html:20 -#, python-format -msgid "" -"See you soon! If you have problems activating your account, feel free to " -"contact us on Facebook at %(facebook_url)s." -msgstr "" -"Hẹn gặp lại bạn! Nếu bạn gặp khó khăn trong việc kích hoạt tài khoản, vui " -"lòng liên hệ với chúng tôi trên Facebook tại %(facebook_url)s." - -#: templates/registration/activation_email.html:23 -#: templates/registration/password_reset_email.html:17 -msgid "See you soon!" -msgstr "Hẹn gặp lại!" - +#: templates/registration/activation_email.html:3 #: templates/registration/activation_email.txt:1 #, python-format msgid "" @@ -7225,38 +6757,53 @@ msgstr "" "Hãy kích hoạt tài khoản %(site_name)s của bạn trong vòng %(expiration_days)d " "tới" +#: templates/registration/activation_email.html:5 +#: templates/registration/activation_email.txt:3 +msgid "Please click on the following link to activate your account:" +msgstr "Hãy nhấn vào link này để kích hoạt tài khoản:" + +#: templates/registration/activation_email.html:10 #: templates/registration/activation_email.txt:6 msgid "Alternatively, you can reply to this message to activate your account." msgstr "Hoặc bạn có thể reply lại email này để kích hoạt." +#: templates/registration/activation_email.html:11 #: templates/registration/activation_email.txt:7 msgid "Your reply must keep the following text intact for this to work:" msgstr "Email reply của bạn cần có đoạn text sau:" +#: templates/registration/activation_email.html:17 +msgid "See you soon!" +msgstr "Hẹn gặp lại!" + +#: templates/registration/activation_email.html:19 +msgid "" +"If you have problems activating your account, feel free to shoot us a " +"message at: " +msgstr "" +"Nếu bạn có vấn đề với việc kích hoạt tài khoản, hãy liên hệ với chúng mình " +"qua: " + #: templates/registration/activation_email_subject.txt:1 #, python-format msgid "Activate your %(SITE_NAME)s account" msgstr "Kích hoạt tài khoản %(SITE_NAME)s" -#: templates/registration/login.html:129 -msgid "Welcome Back" -msgstr "Chào mừng trở lại" - -#: templates/registration/login.html:135 +#: templates/registration/login.html:9 msgid "Invalid username or password." msgstr "Tên người dùng hoặc mật khẩu không hợp lệ." -#: templates/registration/login.html:141 +#: templates/registration/login.html:14 msgid "" "If you have problems with your account, feel free to shoot us a message at: " msgstr "Nếu bạn có vấn đề về tài khoản, hãy liên hệ với chúng mình qua: " -#: templates/registration/login.html:158 +#: templates/registration/login.html:33 #: templates/registration/two_factor_auth.html:92 msgid "Login!" msgstr "Đăng nhập!" -#: templates/registration/login.html:162 +#: templates/registration/login.html:36 msgid "Forgot your password?" msgstr "Quên mật khẩu?" @@ -7264,13 +6811,52 @@ msgstr "Quên mật khẩu?" msgid "See you later!" msgstr "Hẹn gặp lại!" -#: templates/registration/oauth.html:124 +#: templates/registration/oauth.html:31 +msgid "Or log in with..." +msgstr "Hoặc đăng nhập bằng..." + +#: templates/registration/oauth.html:29 msgid "Sign up with Google" msgstr "Đăng ký bằng Gmail" +#: templates/registration/oauth.html:30 +msgid "Or sign up with..." +msgstr "Hoặc đăng ký bằng..." -#: templates/registration/oauth.html:126 -msgid "Or log in with..." -msgstr "Hoặc đăng nhập bằng..." +#: templates/registration/login.html:12 +msgid "Welcome Back" +msgstr "Chào mừng trở lại" + +#: templates/registration/registration_form.html:12 +msgid "Join Our Community" +msgstr "Tham gia cộng đồng" + +#: templates/registration/registration_form.html:45 +msgid "Enter your full name" +msgstr "Nhập họ tên của bạn" + +#: templates/registration/registration_form.html:54 +msgid "Choose a unique username" +msgstr "Chọn tên đăng nhập duy nhất" + +#: templates/registration/registration_form.html:63 +msgid "Enter your email address" +msgstr "Nhập địa chỉ email của bạn" + +#: templates/registration/registration_form.html:75 +msgid "Requirements" +msgstr "Yêu cầu" + +#: templates/registration/registration_form.html:83 +msgid "Enter a secure password" +msgstr "Nhập mật khẩu bảo mật" + +#: templates/registration/registration_form.html:91 +msgid "Confirm Password" +msgstr "Xác nhận mật khẩu" + +#: templates/registration/registration_form.html:94 +msgid "Confirm your password" +msgstr "Xác nhận mật khẩu của bạn" #: templates/registration/password_change_done.html:3 msgid "Your password was successfully changed." @@ -7291,33 +6877,19 @@ msgstr "Để bảo vệ tài khoản của bạn, bạn cần đổi mật kh msgid "Change Password" msgstr "Đổi mật khẩu" -#: templates/registration/password_reset.html:101 -#: templates/registration/password_reset_confirm.html:9 -msgid "Reset Password" -msgstr "Đặt lại mật khẩu" - -#: templates/registration/password_reset.html:104 -#, fuzzy -#| msgid "Password reset complete" -msgid "Password reset is no longer available" -msgstr "Đặt lại mật khẩu thành công" - -#: templates/registration/password_reset.html:105 -msgid "" -"Please login with Google instead. All accounts can now sign in using Google " -"OAuth." -msgstr "" - -#: templates/registration/password_reset.html:112 -#, fuzzy -#| msgid "Enter your email address" -msgid "Email address" -msgstr "Nhập địa chỉ email của bạn" +#: templates/registration/password_reset.html:7 +msgid "Send Reset Email" +msgstr "Gửi email đặt lại mật khẩu" #: templates/registration/password_reset_complete.html:3 msgid "Your password has been set. You may go ahead and log in now." msgstr "Mật khẩu của bạn đã được đặt lại. Giờ bạn có thể đăng nhập." +#: templates/registration/password_reset_confirm.html:9 +#: templates/registration/password_reset_email.html:9 +msgid "Reset Password" +msgstr "Đặt lại mật khẩu" + #: templates/registration/password_reset_confirm.html:12 msgid "Invalid password reset link." msgstr "Đường dẫn đặt lại mật khẩu không hợp lệ." @@ -7335,6 +6907,7 @@ msgid "" msgstr "Nếu bạn không nhận được email, hãy kiểm tra hòm thư rác." #: templates/registration/password_reset_done.html:6 +#: templates/registration/password_reset_email.html:11 msgid "" "See you soon! If you have problems resetting your password, feel free to " "shoot us a message at: " @@ -7346,28 +6919,14 @@ msgstr "" msgid "Forgot your password on the %(site_name)s? Don't worry!" msgstr "Bạn quên mất mật khẩu trên %(site_name)s? Đừng lo lắng!" -#: templates/registration/password_reset_email.html:8 -#, fuzzy, python-format -#| msgid "" -#| "To reset the password for your account \"%(username)s\", click the button " -#| "below." +#: templates/registration/password_reset_email.html:7 +#, python-format msgid "" -"To reset the password for your account \"%(username)s\", click the link " +"To reset the password for your account \"%(username)s\", click the button " "below." msgstr "" "Để đặt lại mật khẩu của tài khoản \"%(username)s\", nhấn vào link phía dưới." -#: templates/registration/password_reset_email.html:14 -#, fuzzy, python-format -#| msgid "" -#| "See you soon! If you have problems resetting your password, feel free to " -#| "shoot us a message at: " -msgid "" -"See you soon! If you have problems resetting your password, feel free to " -"shoot us an email at %(email)s." -msgstr "" -"Nếu bạn có vấn đề với việc đặt lại mật khẩu, hãy liên hệ với chúng mình qua: " - #: templates/registration/password_reset_email.txt:1 #, python-format msgid "" @@ -7423,48 +6982,54 @@ msgstr "" "Nếu bạn có vấn đề với việc kích hoạt tài khoản, hãy liên hệ với chúng mình " "qua: " -#: templates/registration/registration_form.html:251 -msgid "Join Our Community" -msgstr "Tham gia cộng đồng" - -#: templates/registration/registration_form.html:258 +#: templates/registration/registration_form.html:162 +#: templates/registration/registration_form.html:216 msgid "can be blank" msgstr "có thể bỏ trống" -#: templates/registration/registration_form.html:280 +#: templates/registration/registration_form.html:175 msgid "Email" msgstr "Địa chỉ email" -#: templates/registration/registration_form.html:294 -msgid "Requirements" -msgstr "Yêu cầu" +#: templates/registration/registration_form.html:175 +msgid "please choose a popular email provider, e.g. gmail" +msgstr "hãy chọn một địa chỉ mail thông dụng, ví dụ như gmail" -#: templates/registration/registration_form.html:313 -msgid "Confirm Password" -msgstr "Xác nhận mật khẩu" +#: templates/registration/registration_form.html:196 +msgid "(again, for confirmation)" +msgstr "(xác nhận mật khẩu)" + +#: templates/registration/registration_form.html:203 +msgid "(select your closest major city)" +msgstr "(chọn thành phố gần bạn nhất)" -#: templates/registration/registration_form.html:331 +#: templates/registration/registration_form.html:208 msgid "pick from map" msgstr "chọn từ bản đồ" -#: templates/registration/registration_form.html:337 +#: templates/registration/registration_form.html:213 msgid "Default language" msgstr "Ngôn ngữ mặc định" -#: templates/registration/registration_form.html:342 +#: templates/registration/registration_form.html:216 #: templates/user/edit-profile.html:340 msgid "Affiliated organizations" msgstr "Tổ chức đại diện" -#: templates/registration/registration_form.html:353 +#: templates/registration/registration_form.html:225 #: templates/user/edit-profile.html:354 msgid "Notify me about upcoming contests" msgstr "Thông báo cho tôi về các kỳ thi sắp tới" -#: templates/registration/registration_form.html:364 +#: templates/registration/registration_form.html:237 msgid "Register!" msgstr "Đăng ký!" +#: templates/registration/registration_form.html:243 +msgid "By registering, you agree to our [Terms & Conditions][0]." +msgstr "" +"Với việc đăng ký tài khoản, bạn đã đồng ý với [Điều khoản & Điều kiện][0]." + #: templates/registration/totp_disable.html:31 msgid "" "To protect your account, you must first authenticate before you can disable " @@ -7729,31 +7294,31 @@ msgstr "Một lỗi hệ thống vừa xảy ra trong quá trình chấm bài." msgid "Error information" msgstr "Thông tin về lỗi" -#: templates/submission/list.html:86 templates/submission/status.html:84 +#: templates/submission/list.html:34 templates/submission/status.html:84 msgid "Are you sure you want to rejudge?" msgstr "Bạn có chắc bạn muốn chấm lại?" -#: templates/submission/list.html:126 +#: templates/submission/list.html:74 msgid "Filter by status..." msgstr "Lọc theo trạng thái..." -#: templates/submission/list.html:134 +#: templates/submission/list.html:82 msgid "Filter by language..." msgstr "Lọc theo ngôn ngữ..." -#: templates/submission/list.html:378 +#: templates/submission/list.html:314 msgid "Filter submissions" msgstr "Lọc các bài nộp" -#: templates/submission/list.html:401 +#: templates/submission/list.html:337 msgid "Organization" msgstr "Tổ chức" -#: templates/submission/list.html:424 +#: templates/submission/list.html:360 msgid "Total:" msgstr "Tổng:" -#: templates/submission/list.html:442 +#: templates/submission/list.html:370 msgid "You were disconnected. Refresh to show latest updates." msgstr "" "Bạn đã bị ngắt kết nối. Tải lại trang để hiển thị thông tin cập nhật mới " @@ -8101,7 +7666,7 @@ msgstr "Đóng vấn đề" msgid "Reopen ticket" msgstr "Mở lại vấn đề" -#: templates/user/base-users.html:15 templates/user/base-users.html:59 +#: templates/user/base-users.html:15 templates/user/base-users.html:60 #: templates/user/contrib-list.html:15 msgid "Search by handle..." msgstr "Tìm kiếm bằng username" @@ -8440,63 +8005,6 @@ msgstr "Số bài" msgid "Check all" msgstr "Chọn tất cả" -#~ msgid "Rate all users who joined." -#~ msgstr "" -#~ "Tính rating tất cả những người đã tham gia kỳ thi (kể cả không nộp bài " -#~ "nào)." - -#~ msgid "System Status" -#~ msgstr "Trạng thái hệ thống" - -#, python-format -#~ msgid "follow us on %(github)s and %(facebook)s" -#~ msgstr "theo dõi LCOJ trên %(github)s và %(facebook)s" - -#, python-format -#~ msgid "made with love by %(facebook)s" -#~ msgstr "được tạo bởi %(facebook)s" - -#~ msgid "Please read the [guidelines][0] before commenting." -#~ msgstr "Hãy đọc [nội quy][0] trước khi bình luận." - -#~ msgid "" -#~ "If you have problems activating your account, feel free to shoot us a " -#~ "message at: " -#~ msgstr "" -#~ "Nếu bạn có vấn đề với việc kích hoạt tài khoản, hãy liên hệ với chúng " -#~ "mình qua: " - -#~ msgid "Or sign up with..." -#~ msgstr "Hoặc đăng ký bằng..." - -#~ msgid "Enter your full name" -#~ msgstr "Nhập họ tên của bạn" - -#~ msgid "Choose a unique username" -#~ msgstr "Chọn tên đăng nhập duy nhất" - -#~ msgid "Enter a secure password" -#~ msgstr "Nhập mật khẩu bảo mật" - -#~ msgid "Confirm your password" -#~ msgstr "Xác nhận mật khẩu của bạn" - -#~ msgid "Send Reset Email" -#~ msgstr "Gửi email đặt lại mật khẩu" - -#~ msgid "please choose a popular email provider, e.g. gmail" -#~ msgstr "hãy chọn một địa chỉ mail thông dụng, ví dụ như gmail" - -#~ msgid "(again, for confirmation)" -#~ msgstr "(xác nhận mật khẩu)" - -#~ msgid "(select your closest major city)" -#~ msgstr "(chọn thành phố gần bạn nhất)" - -#~ msgid "By registering, you agree to our [Terms & Conditions][0]." -#~ msgstr "" -#~ "Với việc đăng ký tài khoản, bạn đã đồng ý với [Điều khoản & Điều kiện][0]." - #~ msgid "Associated page" #~ msgstr "Trang liên kết" @@ -8521,54 +8029,102 @@ msgstr "Chọn tất cả" #~ msgid "Maximum single-case runtime:" #~ msgstr "Thời gian chạy test lâu nhất:" -#~ msgid "Điều khoản sử dụng" -#~ msgstr "Điều khoản sử dụng" +#: templates/registration/activation_email.html:10 +msgid "The last step is activating your account. Please activate your %(site_name)s account in the next %(count)s day." +msgid_plural "The last step is activating your account. Please activate your %(site_name)s account in the next %(count)s days." +msgstr[0] "Bước cuối cùng là kích hoạt tài khoản của bạn. Vui lòng kích hoạt tài khoản %(site_name)s của bạn trong vòng %(count)s ngày." +msgstr[1] "Bước cuối cùng là kích hoạt tài khoản của bạn. Vui lòng kích hoạt tài khoản %(site_name)s của bạn trong vòng %(count)s ngày." -#~ msgid "Donate" -#~ msgstr "Ủng hộ" +#: templates/registration/activation_email.html:20 +msgid "See you soon! If you have problems activating your account, feel free to contact us on Facebook at %(facebook_url)s." +msgstr "Hẹn gặp lại bạn! Nếu bạn gặp khó khăn trong việc kích hoạt tài khoản, vui lòng liên hệ với chúng tôi trên Facebook tại %(facebook_url)s." + +#: dmoj/urls.py:113 +msgid "Điều khoản sử dụng" +msgstr "Điều khoản sử dụng" + +#: templates/donate-button.html:4 +msgid "Donate" +msgstr "Ủng hộ" + +#: templates/donate-button.html:12 +msgid "Support Luyen Code Online" +msgstr "Ủng hộ Luyện Code Online" + +#: templates/donate-button.html:15 +msgid "Thank you for your interest in supporting us!" +msgstr "Cảm ơn bạn đã quan tâm ủng hộ chúng tôi!" + +#: templates/donate-button.html:16 +msgid "Your donation will be used to:" +msgstr "Khoản ủng hộ của bạn sẽ được sử dụng để:" + +#: templates/donate-button.html:18 +msgid "Maintain and upgrade servers" +msgstr "Duy trì và nâng cấp máy chủ" + +#: templates/donate-button.html:19 +msgid "Expand problem sets and learning materials" +msgstr "Mở rộng bộ đề bài và tài liệu học tập" + +#: templates/donate-button.html:20 +msgid "Improve user experience" +msgstr "Cải thiện trải nghiệm người dùng" + +#: templates/donate-button.html:25 +msgid "QR Code" +msgstr "Mã QR" + +#: templates/donate-button.html:26 +msgid "Scan QR code to transfer" +msgstr "Quét mã QR để chuyển khoản" + +#: templates/donate-button.html:29 +msgid "Thank you so much for your support!" +msgstr "Cảm ơn bạn rất nhiều vì sự ủng hộ!" # Navigation menu items with icons -#~ msgid "🧩 Problems" -#~ msgstr "🧩 Bài tập" +msgid "🧩 Problems" +msgstr "🧩 Bài tập" -#~ msgid "📤 Submissions" -#~ msgstr "📤 Bài nộp" +msgid "📤 Submissions" +msgstr "📤 Bài nộp" -#~ msgid "👥 Users" -#~ msgstr "👥 Người dùng" +msgid "👥 Users" +msgstr "👥 Người dùng" -#~ msgid "🏛️ Organizations" -#~ msgstr "🏛️ Tổ chức" +msgid "🏛️ Organizations" +msgstr "🏛️ Tổ chức" -#~ msgid "🏆 Contests" -#~ msgstr "🏆 Kỳ thi" +msgid "🏆 Contests" +msgstr "🏆 Kỳ thi" -#~ msgid "📚 Resources" -#~ msgstr "📚 Tài nguyên" +msgid "📚 Resources" +msgstr "📚 Tài nguyên" -#~ msgid "🐍 Học Python" -#~ msgstr "🐍 Học Python" +msgid "🐍 Học Python" +msgstr "🐍 Học Python" -#~ msgid "💰 Tính Gross Net" -#~ msgstr "💰 Tính Gross Net" +msgid "💰 Tính Gross Net" +msgstr "💰 Tính Gross Net" -#~ msgid "💵 Tài chính cá nhân" -#~ msgstr "💵 Tài chính cá nhân" +msgid "💵 Tài chính cá nhân" +msgstr "💵 Tài chính cá nhân" -#~ msgid "📝 Blog" -#~ msgstr "📝 Blog" +msgid "📝 Blog" +msgstr "📝 Blog" -#~ msgid "ℹ️ About" -#~ msgstr "ℹ️ Giới thiệu" +msgid "ℹ️ About" +msgstr "ℹ️ Giới thiệu" -#~ msgid "📝 LCOJ docs" -#~ msgstr "📝 Tài liệu LCOJ" +msgid "📝 LCOJ docs" +msgstr "📝 Tài liệu LCOJ" -#~ msgid "🟢 Status" -#~ msgstr "🟢 Trạng thái" +msgid "🟢 Status" +msgstr "🟢 Trạng thái" -#~ msgid "💡 Mẹo" -#~ msgstr "💡 Mẹo" +msgid "💡 Mẹo" +msgstr "💡 Mẹo" -#~ msgid "📘 FAQ" -#~ msgstr "📘 Câu hỏi thường gặp" +msgid "📘 FAQ" +msgstr "📘 Câu hỏi thường gặp" diff --git a/resources/ace-dmoj.scss b/resources/ace-dmoj.scss index 3ff8ed951..6af563a56 100644 --- a/resources/ace-dmoj.scss +++ b/resources/ace-dmoj.scss @@ -3,8 +3,7 @@ .django-ace-widget { display: inline-block; position: relative; - - >div { + > div { position: absolute; top: 0; left: 0; @@ -18,7 +17,7 @@ /* Scroll bar */ ::-webkit-scrollbar-track { - -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); border-radius: 10px; background-color: $color_primary5; } @@ -31,7 +30,7 @@ ::-webkit-scrollbar-thumb { border-radius: 10px; - -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3); + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); background-color: $color_primary66; } } @@ -42,19 +41,19 @@ color: $color_primary66; text-shadow: 0 1px 0 $color_primary0; border-bottom: 1px solid $color_primary25; - background-image: linear-gradient(rgba(0, 0, 0, 0), $color_primary10); + background-image: linear-gradient(rgba(0,0,0,0), $color_primary10); background-repeat: repeat-x; clear: both; overflow: hidden; } .django-ace-max_min { - display: none !important; float: right; padding: 5px; background: url($path_to_root + '/ace/img/expand.png') no-repeat 5px 5px; @include vars-img; + display: block; height: 16px; width: 16px; } @@ -75,4 +74,4 @@ background-image: url($path_to_root + '/ace/img/contract.png'); @include vars-img; } -} \ No newline at end of file +} diff --git a/resources/base.scss b/resources/base.scss index 636f79f37..e6dc9f144 100644 --- a/resources/base.scss +++ b/resources/base.scss @@ -37,13 +37,6 @@ img { width: 100%; } -.full-width-button { - width: 100%; - display: block !important; - text-align: center; - box-sizing: border-box; -} - table.sortable thead { background-color: $color_primary10; color: #666; @@ -244,8 +237,7 @@ noscript #noscript { } } -#form-errors, -.form-errors { +#form-errors, .form-errors { background: rgba(255, 0, 0, 0.3); border: 3px solid red; border-radius: $widget_border_radius; @@ -330,4 +322,4 @@ math { width: auto; padding: 0 5px; } -} \ No newline at end of file +} diff --git a/resources/style.scss b/resources/style.scss index b03ea38ab..187c1fd1c 100644 --- a/resources/style.scss +++ b/resources/style.scss @@ -6,7 +6,6 @@ @use "status"; @use "blog"; @use "problem"; -@use "unified-view"; @use "ticket"; @use "ranks"; @use "users"; @@ -21,4 +20,4 @@ @use "accordion"; @use "select2-dmoj"; @use "ace-dmoj"; -@use "donate"; \ No newline at end of file +@use "donate"; diff --git a/resources/unified-view.scss b/resources/unified-view.scss deleted file mode 100644 index 483d32a8e..000000000 --- a/resources/unified-view.scss +++ /dev/null @@ -1,587 +0,0 @@ -@use "vars" as *; - -body:has(.unified-container) { - - // Unified View Layout - #content { - max-width: 100% !important; - margin: 0 !important; - padding: 0 !important; - width: 100% !important; - } - - #content-body { - padding: 0 !important; - } - - .hidden { - display: none !important; - } - - #tab-description pre { - white-space: pre-wrap; - word-wrap: break-word; - margin: 1.5em 0 1.5em 0; - padding: 1em; - border: 1px solid #ccc; - background-color: #f8f8f8; - color: #000; - border-radius: 4px; - } - - .unified-container { - display: flex; - gap: 0; // Gaps handled by splitjs gutter or internal padding - height: calc(100vh - 80px) !important; // consistent height - margin-top: 1rem; - padding: 0 1rem; - background: $color_primary0; - overflow: hidden; - position: relative; - - @media (max-width: 1024px) { - flex-direction: column; - height: auto !important; - padding-bottom: 2rem; - } - - &.is-fullscreen { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - height: 100vh !important; - z-index: 9999; - margin: 0; - padding: 0; - background: white; - - .split-pane { - height: 100% !important; - } - } - - // Override Global Buttons within Unified View - .button, - button, - input[type=submit] { - background: #555; - background: linear-gradient(to bottom, #666 0, #444 100%) repeat-x; - border-color: #444; - color: white !important; - - &:hover { - background: #444; - background: linear-gradient(to bottom, #555 0, #333 100%) repeat-x; - } - - &:active { - background: #333; - border-color: #222; - } - - &.disabled { - background: #ccc !important; - border-color: #bbb !important; - } - } - - // Icon Buttons (Ghost style) - .btn-icon { - background: transparent !important; - border: 1px solid transparent !important; - color: $color_primary50 !important; - padding: 4px 8px; - box-shadow: none !important; - - &:hover { - background: $color_primary10 !important; - color: $color_primary75 !important; - } - - &:active { - background: $color_primary25 !important; - } - } - } - - .split-pane { - display: flex; - flex-direction: column; - overflow: hidden; - background: $color_primary0; - - &#problem-pane { - flex: 0 0 auto; - width: 50%; - // min-width removed to allow Split.js to handle sizing via minSize - background: $color_primary0; - border-right: 1px solid $color_primary25; - } - - &#submit-pane { - flex: 0 0 auto; - width: 50%; - // min-width removed to allow Split.js to handle sizing via minSize - background: $color_primary5; - display: flex; - flex-direction: column; - height: 100%; - } - } - - // Ensure the form takes full height - #problem_submit { - display: flex; - flex-direction: column; - height: 100%; - flex: 1; - overflow: hidden; - } - - // Editor Container Styling - .editor-container { - flex: 1; - display: flex; - flex-direction: column; - position: relative; - // height: 100%; // Removed to prevent overflow with flex: 1 - - #editor { - flex: 1; - height: 100% !important; - width: 100% !important; - position: absolute !important; - top: 0; - bottom: 0; - left: 0; - right: 0; - - .django-ace-editor, - .django-ace-widget, - .ace_editor { - height: 100% !important; - width: 100% !important; - } - } - } - - // Tabs Navigation - .tabs-nav { - display: flex; - gap: 1.5rem; - padding: 0 1rem 0 0; - background: $color_primary0; - border-bottom: 1px solid $color_primary25; - flex-shrink: 0; - height: 48px; - align-items: center; - - .tab-link { - padding: 0 0.25rem; - cursor: pointer; - transition: all 0.2s ease; - color: $color_primary50; - font-weight: 500; - display: flex; - align-items: center; - gap: 0.5rem; - height: 100%; - border-bottom: 2px solid transparent; - font-size: 0.9rem; - position: relative; - top: 1px; - - i { - font-size: 1rem; - color: $color_primary33; - } - - &:hover { - color: $color_primary75; - - i { - color: $color_primary66; - } - } - - &.is-active { - color: $color_primary90; // Dark grey instead of blue - border-bottom-color: $color_primary90; - - i { - color: $color_primary90; - } - } - } - } - - // Content Areas - .tab-content-container { - flex: 1; - overflow-y: auto; - overflow-x: hidden; - position: relative; - } - - .tab-content { - display: none; - height: 100%; - padding: 1.5rem; - - &.is-active { - display: block; - } - } - - // Problem Meta Header - .problem-header-meta { - margin-bottom: 2rem; - border-bottom: 1px solid $color_primary10; - padding-bottom: 1.5rem; - - .problem-title { - margin: 0 0 1rem 0; - font-size: 1.5rem; - font-weight: 600; - color: $color_primary90; - } - - .meta-row { - display: flex; - flex-wrap: wrap; - gap: 1.5rem; - margin-bottom: 1rem; - } - - .meta-item { - display: inline-flex; - align-items: center; - gap: 0.5rem; - color: $color_primary66; - font-size: 0.9rem; - background: $color_primary5; - padding: 0.25rem 0.75rem; - border-radius: 1rem; - - i { - color: $color_primary33; - } - } - - .meta-actions { - display: flex; - gap: 1rem; - margin-top: 1rem; - - .meta-link { - font-size: 0.9rem; - display: inline-flex; - align-items: center; - gap: 0.4rem; - color: $color_primary66; // Dark grey - text-decoration: none; - - &:hover { - text-decoration: underline; - color: $color_primary90; - } - } - } - } - - // Problem Footer - .problem-footer { - margin-top: 3rem; - padding-top: 2rem; - border-top: 1px solid $color_primary10; - - .footer-item { - margin-bottom: 1rem; - border: 1px solid $color_primary10; - border-radius: 4px; - overflow: hidden; - background: $color_primary0; - - .footer-label { - padding: 0.75rem 1rem; - background: $color_primary5; - cursor: pointer; - display: flex; - align-items: center; - justify-content: space-between; - font-weight: 500; - color: $color_primary75; - user-select: none; - - .toggle-icon { - transition: transform 0.2s; - font-size: 0.8rem; - color: $color_primary33; - } - - &:hover { - background: $color_primary10; - } - } - - .footer-content { - display: none; - padding: 1rem; - border-top: 1px solid $color_primary10; - font-size: 0.9rem; - color: $color_primary66; - line-height: 1.6; - } - - &.is-open { - .footer-label .toggle-icon { - transform: rotate(90deg); - } - - .footer-content { - display: block; - } - } - } - - .lang-badge { - display: inline-block; - padding: 0.25rem 0.5rem; - background: $color_primary5; - border: 1px solid $color_primary10; - border-radius: 3px; - margin: 0 0.25rem 0.25rem 0; - font-size: 0.85rem; - color: $color_primary66; - - &.disabled { - opacity: 0.6; - text-decoration: line-through; - } - } - } - - // Editor Overrides - #editor-container { - border: none !important; - border-radius: 0 !important; - } - - .ace_editor { - border: none !important; - } - - // Responsive - @media (max-width: 768px) { - .tabs-nav { - overflow-x: auto; - padding: 0 1rem; - gap: 1rem; - - .tab-link { - white-space: nowrap; - } - } - - .problem-header-meta { - .meta-row { - gap: 0.5rem; - } - } - } - - // Split.js Gutter Styles - .gutter { - background-color: $color_primary10; - background-repeat: no-repeat; - background-position: 50%; - z-index: 50; // Reduced from 9999 to avoid overlapping navbar - position: relative; - - &.gutter-horizontal { - cursor: col-resize; - transition: background-color 0.2s; - - &:hover { - background-color: #999; // Neutral grey highlight on hover - } - - &.gutter-active { - // When dragging - background-color: #777; - } - } - } - - // Editor Toolbar Layout - .editor-header { - display: flex; - justify-content: space-between; - align-items: center; - background: $color_primary0; - border-bottom: 1px solid $color_primary25; - height: 48px; // Match tabs-nav height - min-height: 48px; // Enforce minimum - flex-shrink: 0; // Prevent shrinking - - .header-left, - .header-right { - display: flex; - align-items: center; - gap: 0.5rem; - height: 100%; - } - - .header-right { - margin-left: auto; - } - } - - // Spoiler Protection (Editorial & Comments) - .editorial-content { - - // Keep specific styling for editorial headers if needed, but wrapper is generic - h3 { - margin-top: 0; - margin-bottom: 1.5rem; - color: $color_primary90; - } - } - - .spoiler-wrapper { - position: relative; - overflow: hidden; - border-radius: 8px; - border: 1px solid $color_primary10; - background: $color_primary0; - } - - .spoiler-body { - padding: 1.5rem; - transition: filter 0.4s ease, opacity 0.4s ease; - - &.blurred { - filter: blur(12px); - opacity: 0.5; - user-select: none; - pointer-events: none; - max-height: 60vh; - overflow: hidden; - } - } - - // Reorder Comments Tab - #tab-comments .comment-area { - display: flex; - flex-direction: column; - - // Elements with default order (0) will appear first: - // - h2 (Title) - // - .alert (Guidelines) - // - hr, br - - #new-comment { - order: 1; - margin-bottom: 2rem; - padding: 0; // Remove default padding if needed to align left - - // Constrain height of the comment editor - #comment-form-body { - - .django-ace-editor, - .django-ace-widget, - .ace_editor { - height: 200px !important; - min-height: 200px !important; - } - - textarea { - height: 200px !important; - } - } - } - - ul.comments { - order: 2; - } - } - - .spoiler-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - background: rgba($color_primary0, 0.4); - backdrop-filter: blur(4px); - z-index: 10; - padding: 1rem; - - .spoiler-reveal-container { - background: $color_primary0; - padding: 2rem; - border-radius: 12px; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15), 0 0 0 1px $color_primary10; - max-width: 420px; - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - gap: 1.5rem; - pointer-events: auto; - animation: spoiler-pop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); - } - - .spoiler-warning { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; - - i { - font-size: 2rem; - color: #f59e0b; // Amber 500 - margin-bottom: 0.5rem; - } - - h3 { - margin: 0; - font-size: 1.15rem; - font-weight: 600; - color: $color_primary90; - } - - p { - margin: 0; - font-size: 0.9rem; - line-height: 1.5; - color: $color_primary66; - } - } - - .reveal-btn { - width: 100%; - justify-content: center; - padding: 0.75rem; - font-size: 0.95rem; - } - } - - @keyframes spoiler-pop { - from { - opacity: 0; - transform: scale(0.95); - } - - to { - opacity: 1; - transform: scale(1); - } - } -} \ No newline at end of file diff --git a/templates/problem/problem.html b/templates/problem/problem.html index aa82c5b21..c27e8c977 100644 --- a/templates/problem/problem.html +++ b/templates/problem/problem.html @@ -1,26 +1,55 @@ {% extends "common-content.html" %} - - - {% block content_media %} {% if not comment_lock %} {% include "comments/media-css.html" %} {% endif %} {% endblock %} @@ -29,10 +58,6 @@ {% if not comment_lock %} {% include "comments/media-js.html" %} {% endif %} - {% if form %} - - {{ form.media.js }} - {% endif %} - - {% endblock %} {% block title_row %} - {% if False %} -
{% if request.user.is_authenticated %} {% if problem.id in completed_problem_ids %} @@ -204,32 +120,29 @@

{{ problem.code|upper }} - {{ title }}

{{ _('View as PDF') }}
- {% endif %} {% endblock %} {% block info_float %} - {% if not form or is_mobile %} - {% if request.user.is_authenticated and request.in_contest and submission_limit %} - {% if submissions_left > 0 %} - - {{ _('Submit solution') }} - -
- {% trans trimmed counter=submissions_left %} - {{ counter }} submission left - {% pluralize %} - {{ counter }} submissions left - {% endtrans %} -
- {% else %} - {{ _('Submit solution') }} -
{{ _('0 submissions left') }}
- {% endif %} - {% else %} - + {% if request.user.is_authenticated and request.in_contest and submission_limit %} + {% if submissions_left > 0 %} + {{ _('Submit solution') }} +
+ {% trans trimmed counter=submissions_left %} + {{ counter }} submission left + {% pluralize %} + {{ counter }} submissions left + {% endtrans %} +
+ {% else %} + {{ _('Submit solution') }} +
{{ _('0 submissions left') }}
{% endif %} + {% else %} + + {{ _('Submit solution') }} + {% endif %}
@@ -488,11 +401,3 @@

{{ _('Clarifications') }}

{% endwith %} {% endif %} {% endblock %} - -{% block body %} - {% if is_mobile %} - {{ super() }} - {% else %} - {% include "problem/unified_view.html" %} - {% endif %} -{% endblock %} diff --git a/templates/problem/submit_js.html b/templates/problem/submit_js.html deleted file mode 100644 index 5e4aac3e0..000000000 --- a/templates/problem/submit_js.html +++ /dev/null @@ -1,289 +0,0 @@ -{% compress js %} - -{% endcompress %} diff --git a/templates/problem/submit_partial.html b/templates/problem/submit_partial.html deleted file mode 100644 index c85371deb..000000000 --- a/templates/problem/submit_partial.html +++ /dev/null @@ -1,237 +0,0 @@ -{% if not no_judges %} - {% if default_lang not in form.fields.language.queryset %} -
- {{ _('Warning!') }} - {{ _('Your default language, %(language)s, is unavailable for this problem and has been deselected.', language=bold(default_lang.name)) }} -
- {% endif %} - - {% if request.in_contest and submission_limit %} - {% if submissions_left > 0 %} -
- {% trans left=submissions_left -%} - You have {{ left}} submission left - {%- pluralize -%} - You have {{ left }} submissions left - {%- endtrans %} -
- {% else %} -
- {{ _('You have 0 submissions left') }} -
- {% endif %} - {% endif %} -{% endif %} - -
- {% csrf_token %} - {{ form.non_field_errors() }} - - {# Sticky toolbar with key actions #} - {# Header with All Tools #} -
-
- {% if not no_judges %} -
- - - - - -
- -
-
- {% for lang in form.fields.language.queryset %} -
- - {{ lang.name }} -
- {% endfor %} -
-
-
-
- - - - {% endif %} -
- -
- - - - {% if not no_judges %} - {{ form.judge }} - - {% if request.user.is_authenticated %} - - {% else %} - - {{ _('Submit') }} - - {% endif %} - {% endif %} -
-
- - {# Editor container #} -
- {% if no_judges %} -
- {{ _('No judge is available for this problem.') }} -
- {% else %} - - - {% endif %} -
-
diff --git a/templates/problem/unified_view.html b/templates/problem/unified_view.html deleted file mode 100644 index 471eae321..000000000 --- a/templates/problem/unified_view.html +++ /dev/null @@ -1,257 +0,0 @@ -
-
-
- - {% if editorial and editorial.is_accessible_by(request.user) %} - - {% endif %} - - {% if not comment_lock %} - - {% endif %} - -
- -
- -
-
-

{{ problem.code|upper }} - {{ problem.name }}

-
- - {{ problem.points|floatformat(2) }} - - - {{ problem.time_limit }}s - - - {{ problem.memory_limit|kbsimpleformat }} - - - - {% if problem.io_method.method == 'standard' %}Standard{% else %}File{% endif %} - -
- - -
- {% if has_pdf_render %} - PDF - {% endif %} - - {% if can_edit_problem %} - Edit - {% endif %} -
-
- - - -
- {% include "problem/problem-detail.html" %} -
- - -
- - - {% if editorial %} -
-
-

{{ _('Editorial') }}

-
-
- {{ editorial.content|markdown('problem', MATH_ENGINE)|reference|str|safe }} -
-
-
-
- -

{{ _('Warning: Spoilers Ahead') }}

-

{{ _('This editorial contains hints and solutions. Are you sure you want to see it?') }}

-
- -
-
-
-
-
- {% endif %} - - -
-

{{ _('My Submissions') }}

- {% if request.user.is_authenticated %} - {% if my_submissions %} -
- {% set profile_id = request.profile.id if request.user.is_authenticated else 0 %} - {% for submission in my_submissions %} -
- {% with problem_name=false %} - {% include "submission/row.html" %} - {% endwith %} -
- {% endfor %} - -
- {% else %} -

{{ _('No submissions yet.') }}

- {% endif %} - {% else %} -

{{ _('Please log in to view submissions.') }}

- {% endif %} -
- - - {% if not comment_lock %} -
-
-
- {% with comment_warn_code=true %} - {% include "comments/list.html" %} - {% endwith %} -
-
-
-
- -

{{ _('Warning: Community Discussion') }}

-

{{ _('Comments may contain user-submitted hints and solutions. Proceed with caution.') }}

-
- -
-
-
-
- {% endif %} -
-
- - -
- {% include "problem/submit_partial.html" %} - {% include "problem/submit_js.html" %} - -
-
- - From 6e1743fb68fa274353ebb3659d0fdf8d90352269 Mon Sep 17 00:00:00 2001 From: Hieu Date: Thu, 29 Jan 2026 22:39:53 +0700 Subject: [PATCH 2/2] fix lint --- .../commands/generate_editorials.py | 312 +++++++++--------- judge/migrations/0215_populate_vote_counts.py | 16 +- judge/views/comment.py | 2 +- judge/views/problem.py | 62 ++++ 4 files changed, 220 insertions(+), 172 deletions(-) diff --git a/judge/management/commands/generate_editorials.py b/judge/management/commands/generate_editorials.py index 70312581d..446a4c8cd 100644 --- a/judge/management/commands/generate_editorials.py +++ b/judge/management/commands/generate_editorials.py @@ -7,50 +7,48 @@ import logging import time -import json -from typing import List, Optional -from datetime import datetime +from typing import List from django.core.management.base import BaseCommand, CommandError from django.db import transaction from django.utils import timezone -from judge.models import Problem, Solution, Submission, SubmissionSource, Profile +from judge.models import Problem, Solution, Submission try: from pydantic import BaseModel, Field except ImportError: - raise CommandError("Pydantic not installed. Run: pip install pydantic") + raise CommandError('Pydantic not installed. Run: pip install pydantic') # ==================== PYDANTIC MODELS FOR STRUCTURED OUTPUT ==================== class Approach(BaseModel): """Represents a solution approach.""" - name: str = Field(description="Name of the approach (e.g., 'Brute Force', 'Hash Map', 'Two Pointers')") - language: str = Field(description="Programming language used") - code: str = Field(description="Code snippet for this approach") - time_complexity: str = Field(description="Time complexity in Big-O notation (e.g., 'O(n)', 'O(n log n)')") - space_complexity: str = Field(description="Space complexity in Big-O notation (e.g., 'O(1)', 'O(n)')") - explanation: str = Field(description="Detailed explanation of how this approach works") + name: str = Field(description='Name of the approach (e.g., "Brute Force", "Hash Map", "Two Pointers")') + language: str = Field(description='Programming language used') + code: str = Field(description='Code snippet for this approach') + time_complexity: str = Field(description='Time complexity in Big-O notation (e.g., "O(n)", "O(n log n)")') + space_complexity: str = Field(description='Space complexity in Big-O notation (e.g., "O(1)", "O(n)")') + explanation: str = Field(description='Detailed explanation of how this approach works') class EditorialContent(BaseModel): """Structured editorial content.""" problem_understanding: str = Field( - description="Clear explanation of what the problem asks and the key concepts" + description='Clear explanation of what the problem asks and the key concepts', ) approaches: List[Approach] = Field( - description="List of solution approaches, from simplest to most optimal", - min_items=1 + description='List of solution approaches, from simplest to most optimal', + min_items=1, ) key_insights: List[str] = Field( - description="Key insights and patterns to recognize similar problems", - min_items=1 + description='Key insights and patterns to recognize similar problems', + min_items=1, ) common_pitfalls: List[str] = Field( - description="Common mistakes and edge cases to watch out for", - min_items=1 + description='Common mistakes and edge cases to watch out for', + min_items=1, ) @@ -88,25 +86,25 @@ def client(self): if not api_key: raise CommandError( - "OPENAI_API_KEY environment variable not set. " - "Please set it with: export OPENAI_API_KEY='sk-...'" + 'OPENAI_API_KEY environment variable not set. ' + 'Please set it with: export OPENAI_API_KEY="sk-..."', ) if base_url: - self.logger.info(f"Using custom OpenAI endpoint: {base_url}") + self.logger.info('Using custom OpenAI endpoint: %s', base_url) self._client = OpenAI( base_url=base_url, - api_key=api_key + api_key=api_key, ) else: self._client = OpenAI(api_key=api_key) except ImportError: raise CommandError( - "OpenAI package not installed. Run: pip install openai" + 'OpenAI package not installed. Run: pip install openai', ) except Exception as e: - raise CommandError(f"Failed to initialize OpenAI client: {e}") + raise CommandError(f'Failed to initialize OpenAI client: {e}') return self._client def _setup_logging(self, log_file=None): @@ -118,7 +116,7 @@ def _setup_logging(self, log_file=None): logging.basicConfig( level=logging.DEBUG if self.verbose else logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', - handlers=handlers + handlers=handlers, ) self.logger = logging.getLogger(__name__) @@ -140,9 +138,9 @@ def get_ac_solutions(self, problem, limit=3): problem=problem, result='AC', status='D', - language__name__regex=r'^C*$' # C, C++, C++17, C++20, C11, etc. + language__name__regex=r'^C*$', # C, C++, C++17, C++20, C11, etc. ).select_related( - 'user', 'user__user', 'language', 'source' + 'user', 'user__user', 'language', 'source', ).order_by('-id') seen = set() @@ -169,26 +167,26 @@ def validate_generation(self, problem, solutions): errors = [] if not problem: - errors.append("Problem is None") + errors.append('Problem is None') return errors if not problem.is_public: - errors.append(f"Problem {problem.code} is not public") + errors.append(f'Problem {problem.code} is not public') if Solution.objects.filter(problem=problem).exists(): - errors.append(f"Editorial already exists for {problem.code}") + errors.append(f'Editorial already exists for {problem.code}') if len(solutions) < 1: - errors.append(f"Insufficient AC C/C++ solutions for {problem.code}: {len(solutions)} (need at least 1)") + errors.append(f'Insufficient AC C/C++ solutions for {problem.code}: {len(solutions)} (need at least 1)') for i, sub in enumerate(solutions): if not hasattr(sub, 'source') or not sub.source: - errors.append(f"Submission {sub.id} has no source code") + errors.append(f'Submission {sub.id} has no source code') continue source_code = sub.source.source if not source_code or len(source_code.strip()) < 10: - errors.append(f"Submission {sub.id} source code too short") + errors.append(f'Submission {sub.id} source code too short') return errors @@ -196,68 +194,65 @@ def format_solutions_for_prompt(self, solutions): """Format solutions for OpenAI prompt.""" formatted = [] for i, sub in enumerate(solutions, 1): - language = sub.language.common_name if sub.language else "Unknown" - username = sub.user.user.username if sub.user and sub.user.user else "Unknown" - source = sub.source.source if sub.source else "No source" + language = sub.language.common_name if sub.language else 'Unknown' + username = sub.user.user.username if sub.user and sub.user.user else 'Unknown' + source = sub.source.source if sub.source else 'No source' if len(source) > 1000: - source = source[:1000] + "\n// ... (truncated)" + source = source[:1000] + '\n// ... (truncated)' # Normalize language codes for code blocks lang_code = language.lower().replace('c++', 'cpp').replace('c#', 'csharp') formatted.append( - f"--- Solution {i} ---\n" - f"Language: {language}\n" - f"User: {username}\n" - f"Submission ID: {sub.id}\n" - f"Code:\n```{lang_code}\n{source}\n```\n" + f'--- Solution {i} ---\n' + f'Language: {language}\n' + f'User: {username}\n' + f'Submission ID: {sub.id}\n' + f'Code:\n```{lang_code}\n{source}\n```\n', ) - return "\n".join(formatted) + return '\n'.join(formatted) def build_openai_prompt(self, problem, formatted_solutions): """Build the prompt for OpenAI with structured output requirements.""" - description = problem.description or "No description available" - - prompt = f"""You are an expert in competitive programming education. Analyze the problem and solutions, then generate a detailed editorial in Vietnamese. - -## Problem Information -**Code**: {problem.code} -**Name**: {problem.name} -**Description**: {description} - -## Accepted Solutions -{formatted_solutions} - -## Instructions -Analyze the solutions above and create structured editorial data: -1. Explain the problem clearly in Vietnamese -2. Identify 2-3 different approaches from the solutions -3. Order from simplest to most optimal -4. Provide code, complexity, and explanation for each approach -5. List key insights and common pitfalls - -## Output Requirements -You MUST return valid JSON following this schema: -{{ - "problem_understanding": "string - clear explanation in Vietnamese", - "approaches": [ - {{ - "name": "string - approach name (e.g., 'Brute Force', 'Hash Map')", - "language": "string - programming language (lowercase, use 'cpp' for C++, 'csharp' for C#)", - "code": "string - code snippet", - "time_complexity": "string - e.g., O(n), O(n log n), can use ~10^9~ if needed", - "space_complexity": "string - e.g., O(1), O(n), can use ~10^9~ if needed", - "explanation": "string - detailed explanation in Vietnamese" - }} - ], - "key_insights": ["string - insight 1", "string - insight 2"], - "common_pitfalls": ["string - pitfall 1", "string - pitfall 2"] -}} - -DO NOT include any text outside the JSON object. -""" + description = problem.description or 'No description available' + + prompt = ( + f'You are an expert in competitive programming education. ' + f'Analyze the problem and solutions, then generate a detailed editorial in Vietnamese.\n\n' + f'## Problem Information\n' + f'**Code**: {problem.code}\n' + f'**Name**: {problem.name}\n' + f'**Description**: {description}\n\n' + f'## Accepted Solutions\n' + f'{formatted_solutions}\n\n' + f'## Instructions\n' + f'Analyze the solutions above and create structured editorial data:\n' + f'1. Explain the problem clearly in Vietnamese\n' + f'2. Identify 2-3 different approaches from the solutions\n' + f'3. Order from simplest to most optimal\n' + f'4. Provide code, complexity, and explanation for each approach\n' + f'5. List key insights and common pitfalls\n\n' + f'## Output Requirements\n' + f'You MUST return valid JSON following this schema:\n' + f'{{\n' + f' "problem_understanding": "string - clear explanation in Vietnamese",\n' + f' "approaches": [\n' + f' {{\n' + f' "name": "string - approach name (e.g., \'Brute Force\', \'Hash Map\')",\n' + f' "language": "string - programming language (lowercase, use \'cpp\' for C++, \'csharp\' for C#)",\n' + f' "code": "string - code snippet",\n' + f' "time_complexity": "string - e.g., O(n), O(n log n), can use ~10^9~ if needed",\n' + f' "space_complexity": "string - e.g., O(1), O(n), can use ~10^9~ if needed",\n' + f' "explanation": "string - detailed explanation in Vietnamese"\n' + f' }}\n' + f' ],\n' + f' "key_insights": ["string - insight 1", "string - insight 2"],\n' + f' "common_pitfalls": ["string - pitfall 1", "string - pitfall 2"]\n' + f'}}\n\n' + f'DO NOT include any text outside the JSON object.\n' + ) return prompt def call_openai_structured(self, prompt, attempt=0): @@ -267,11 +262,14 @@ def call_openai_structured(self, prompt, attempt=0): response = self.client.chat.completions.parse( model=self.model, messages=[ - {"role": "system", "content": "You are a competitive programming education expert. Always return valid JSON."}, - {"role": "user", "content": prompt} + { + 'role': 'system', + 'content': 'You are a competitive programming education expert. Always return valid JSON.', + }, + {'role': 'user', 'content': prompt}, ], temperature=self.temperature, - response_format=EditorialContent + response_format=EditorialContent, ) # Get the parsed result directly @@ -281,8 +279,9 @@ def call_openai_structured(self, prompt, attempt=0): except Exception as e: if attempt < self.max_retries: wait_time = self.retry_delay * (2 ** attempt) - self.logger.warning(f"API error (attempt {attempt + 1}/{self.max_retries}): {e}") - self.logger.info(f"Retrying in {wait_time} seconds...") + err_msg = str(e) + self.logger.warning('API error (attempt %d/%d): %s', attempt + 1, self.max_retries, err_msg) + self.logger.info('Retrying in %d seconds...', wait_time) time.sleep(wait_time) return self.call_openai_structured(prompt, attempt + 1) else: @@ -290,18 +289,18 @@ def call_openai_structured(self, prompt, attempt=0): def generate_editorial_content(self, problem, solutions): """Generate editorial content using OpenAI with structured output.""" - self.logger.info(f"Generating editorial for {problem.code}...") + self.logger.info('Generating editorial for %s...', problem.code) formatted_solutions = self.format_solutions_for_prompt(solutions) prompt = self.build_openai_prompt(problem, formatted_solutions) if self.verbose: - self.logger.debug(f"Prompt length: {len(prompt)} characters") + self.logger.debug('Prompt length: %d characters', len(prompt)) editorial = self.call_openai_structured(prompt) if self.verbose: - self.logger.debug(f"Generated structured editorial") + self.logger.debug('Generated structured editorial') return editorial @@ -309,53 +308,53 @@ def format_editorial_to_markdown(self, editorial: EditorialContent, problem): """Convert Pydantic EditorialContent to markdown format with Vietnamese headers.""" lines = [] # lines.append(f"# Editorial for {problem.code}: {problem.name}") - lines.append("") - lines.append("## Hiểu bài toán") + lines.append('') + lines.append('## Hiểu bài toán') lines.append(editorial.problem_understanding) - lines.append("") - lines.append("## Các cách tiếp cận") + lines.append('') + lines.append('## Các cách tiếp cận') for i, approach in enumerate(editorial.approaches, 1): # Normalize language code lang_code = approach.language.lower().replace('c++', 'cpp').replace('c#', 'csharp') - lines.append(f"### Cách {approach.name}") - lines.append("") - lines.append(f"```{lang_code}") + lines.append(f'### Cách {approach.name}') + lines.append('') + lines.append(f'```{lang_code}') lines.append(approach.code) - lines.append("```") - lines.append("") - lines.append(f"* **Time Complexity**: {approach.time_complexity}") - lines.append(f"* **Space Complexity**: {approach.space_complexity}") - lines.append("") + lines.append('```') + lines.append('') + lines.append(f'* **Time Complexity**: {approach.time_complexity}') + lines.append(f'* **Space Complexity**: {approach.space_complexity}') + lines.append('') lines.append(approach.explanation) - lines.append("") + lines.append('') - lines.append("## Phân tích độ phức tạp") - lines.append("| Cách tiếp cận | Time | Space | Tên |") - lines.append("|--------------|------|-------|-----|") + lines.append('## Phân tích độ phức tạp') + lines.append('| Cách tiếp cận | Time | Space | Tên |') + lines.append('|--------------|------|-------|-----|') for i, approach in enumerate(editorial.approaches, 1): - lines.append(f"| {i} | {approach.time_complexity} | {approach.space_complexity} | {approach.name} |") - lines.append("") + lines.append(f'| {i} | {approach.time_complexity} | {approach.space_complexity} | {approach.name} |') + lines.append('') - lines.append("## Bài học kinh nghiệm") + lines.append('## Bài học kinh nghiệm') for insight in editorial.key_insights: - lines.append(f"- {insight}") - lines.append("") + lines.append(f'- {insight}') + lines.append('') - lines.append("## Lỗi thường gặp") + lines.append('## Lỗi thường gặp') for pitfall in editorial.common_pitfalls: - lines.append(f"- {pitfall}") + lines.append(f'- {pitfall}') - return "\n".join(lines) + return '\n'.join(lines) def save_editorial(self, problem, editorial: EditorialContent, solutions, dry_run=False): """Save editorial to database.""" content = self.format_editorial_to_markdown(editorial, problem) if dry_run: - self.logger.info(f"[DRY RUN] Would create editorial for {problem.code}") - self.logger.info(f"Content preview:\n{content[:500]}...") + self.logger.info('[DRY RUN] Would create editorial for %s', problem.code) + self.logger.info('Content preview:\n%s...', content[:500]) return None # Get admin user as first author @@ -363,7 +362,7 @@ def save_editorial(self, problem, editorial: EditorialContent, solutions, dry_ru try: admin_user = User.objects.get(username='admin') admin_profile = admin_user.profile - except: + except User.DoesNotExist: # Fallback: get any superuser admin_user = User.objects.filter(is_superuser=True).first() if admin_user: @@ -392,7 +391,7 @@ def save_editorial(self, problem, editorial: EditorialContent, solutions, dry_ru problem=problem, content=content, is_public=True, - publish_on=timezone.now() + publish_on=timezone.now(), ) for author in authors: @@ -400,36 +399,36 @@ def save_editorial(self, problem, editorial: EditorialContent, solutions, dry_ru solution.save() - self.logger.info(f"✓ Created editorial for {problem.code} (ID: {solution.id})") - self.logger.info(f" - Authors: {', '.join(a.user.username for a in authors)}") - self.logger.info(f" - Status: Public") - self.logger.info(f" - Content: {len(content)} chars, {len(editorial.approaches)} approaches") + self.logger.info('✓ Created editorial for %s (ID: %s)', problem.code, solution.id) + self.logger.info(' - Authors: %s', ', '.join(a.user.username for a in authors)) + self.logger.info(' - Status: Public') + self.logger.info(' - Content: %d chars, %d approaches', len(content), len(editorial.approaches)) return solution - except Exception as e: - self.logger.error(f"✗ Failed to save editorial for {problem.code}: {e}") + except Exception: + self.logger.exception('✗ Failed to save editorial for %s', problem.code) raise def process_problem(self, problem): """Process a single problem.""" - self.logger.info(f"\n{'='*60}") - self.logger.info(f"Processing: {problem.code} - {problem.name}") - self.logger.info(f"{'='*60}") + self.logger.info('\n%s', '=' * 60) + self.logger.info('Processing: %s - %s', problem.code, problem.name) + self.logger.info('=' * 60) solutions = self.get_ac_solutions(problem, limit=3) - self.logger.info(f"Found {len(solutions)} AC solutions") + self.logger.info('Found %d AC solutions', len(solutions)) for i, sub in enumerate(solutions, 1): - lang = sub.language.common_name if sub.language else "Unknown" - user = sub.user.user.username if sub.user and sub.user.user else "Unknown" - self.logger.info(f" {i}. {lang} by {user} (ID: {sub.id})") + lang = sub.language.common_name if sub.language else 'Unknown' + user = sub.user.user.username if sub.user and sub.user.user else 'Unknown' + self.logger.info(' %d. %s by %s (ID: %s)', i, lang, user, sub.id) errors = self.validate_generation(problem, solutions) if errors: - self.logger.warning(f"✗ Validation failed for {problem.code}:") + self.logger.warning('✗ Validation failed for %s:', problem.code) for error in errors: - self.logger.warning(f" - {error}") + self.logger.warning(' - %s', error) return False try: @@ -437,16 +436,16 @@ def process_problem(self, problem): result = self.save_editorial(problem, editorial, solutions, self.dry_run) if self.dry_run: - self.logger.info(f"✓ Dry run successful: {problem.code}") + self.logger.info('✓ Dry run successful: %s', problem.code) return True elif result: - self.logger.info(f"✓ Success: {problem.code}") + self.logger.info('✓ Success: %s', problem.code) return True else: return False - except Exception as e: - self.logger.error(f"✗ Failed to generate editorial for {problem.code}: {e}") + except Exception: + self.logger.exception('✗ Failed to generate editorial for %s', problem.code) return False @@ -469,7 +468,7 @@ def handle(self, *args, **options): import os if not os.environ.get('OPENAI_API_KEY'): self.stdout.write(self.style.WARNING( - "WARNING: OPENAI_API_KEY not set. Set with: export OPENAI_API_KEY='sk-...'\n" + 'WARNING: OPENAI_API_KEY not set. Set with: export OPENAI_API_KEY="sk-..."\n', )) generator = EditorialGenerator(self, **options) @@ -478,30 +477,30 @@ def handle(self, *args, **options): problems = generator.get_problems_without_editorials( problem_code=options['problem'], limit=1, - offset=0 + offset=0, ) if not problems: self.stdout.write(self.style.ERROR( - f"Problem '{options['problem']}' not found or already has editorial" + f'Problem \'{options["problem"]}\' not found or already has editorial', )) return else: problems = generator.get_problems_without_editorials( limit=options['limit'], - offset=options['offset'] + offset=options['offset'], ) if not problems: - self.stdout.write(self.style.NOTICE("No problems found to process.")) + self.stdout.write(self.style.NOTICE('No problems found to process.')) return self.stdout.write( - self.style.SUCCESS(f"Found {len(problems)} problem(s) to process\n") + self.style.SUCCESS(f'Found {len(problems)} problem(s) to process\n'), ) if options['dry_run']: - self.stdout.write(self.style.WARNING("=== DRY RUN MODE ===")) - self.stdout.write("No changes will be saved to database\n") + self.stdout.write(self.style.WARNING('=== DRY RUN MODE ===')) + self.stdout.write('No changes will be saved to database\n') results = {'success': 0, 'failed': 0, 'skipped': 0} @@ -512,23 +511,8 @@ def handle(self, *args, **options): results['success'] += 1 else: results['failed'] += 1 - except Exception as e: - generator.logger.error(f"Unexpected error processing {problem.code}: {e}") + except Exception: + generator.logger.exception('Unexpected error processing %s', problem.code) results['failed'] += 1 - self.stdout.write("\n" + "="*60) - self.stdout.write(self.style.SUCCESS("SUMMARY")) - self.stdout.write("="*60) - self.stdout.write(f"Processed: {len(problems)}") - self.stdout.write(f"Success: {self.style.SUCCESS(str(results['success']))}") - self.stdout.write(f"Failed: {self.style.ERROR(str(results['failed']))}") - - if options['dry_run']: - self.stdout.write("\n" + self.style.WARNING( - "This was a dry run. No changes were saved.\n" - "Run without --dry-run to actually create editorials." - )) - else: - self.stdout.write("\n" + self.style.SUCCESS( - "Generated editorials are set to PUBLIC (is_public=True)." - )) + self.stdout.write('\n' + '=' * 60) diff --git a/judge/migrations/0215_populate_vote_counts.py b/judge/migrations/0215_populate_vote_counts.py index b10908985..7f0a9bfb1 100644 --- a/judge/migrations/0215_populate_vote_counts.py +++ b/judge/migrations/0215_populate_vote_counts.py @@ -1,7 +1,8 @@ from django.db import migrations -from django.db.models import Count, OuterRef, Subquery, IntegerField +from django.db.models import Count, IntegerField, OuterRef, Subquery from django.db.models.functions import Coalesce + def populate_votes(apps, schema_editor): Comment = apps.get_model('judge', 'Comment') CommentVote = apps.get_model('judge', 'CommentVote') @@ -9,25 +10,26 @@ def populate_votes(apps, schema_editor): # Subquery to count upvotes (score = 1) upvotes_qs = CommentVote.objects.filter( comment=OuterRef('pk'), - score=1 + score=1, ).values('comment').annotate( - count=Count('id') + count=Count('id'), ).values('count') - + # Subquery to count downvotes (score = -1) downvotes_qs = CommentVote.objects.filter( comment=OuterRef('pk'), - score=-1 + score=-1, ).values('comment').annotate( - count=Count('id') + count=Count('id'), ).values('count') # Update all comments efficiently Comment.objects.update( upvotes=Coalesce(Subquery(upvotes_qs, output_field=IntegerField()), 0), - downvotes=Coalesce(Subquery(downvotes_qs, output_field=IntegerField()), 0) + downvotes=Coalesce(Subquery(downvotes_qs, output_field=IntegerField()), 0), ) + class Migration(migrations.Migration): dependencies = [ diff --git a/judge/views/comment.py b/judge/views/comment.py index bc9f67925..d5383af1b 100644 --- a/judge/views/comment.py +++ b/judge/views/comment.py @@ -77,7 +77,7 @@ def vote_comment(request, delta): # Update vote counts and ranking score comment = Comment.objects.get(id=comment_id) comment.vote(delta) - + # Update upvote/downvote counts if delta == 1: comment.upvotes = F('upvotes') + 1 diff --git a/judge/views/problem.py b/judge/views/problem.py index 30b4fa60f..7f87e87fe 100755 --- a/judge/views/problem.py +++ b/judge/views/problem.py @@ -190,6 +190,12 @@ def get_comment_page(self): def get_context_data(self, **kwargs): context = super(ProblemDetail, self).get_context_data(**kwargs) + + # Detect mobile user agent + ua = self.request.META.get('HTTP_USER_AGENT', '').lower() + # 'mobi' covers iPhone, Android mobile, etc. iPad and Android tablets usually exclude 'mobi'. + context['is_mobile'] = 'mobi' in ua + user = self.request.user authed = user.is_authenticated contest_problem = self.contest_problem @@ -243,6 +249,62 @@ def get_context_data(self, **kwargs): context['description'], 'problem') context['meta_description'] = self.object.summary or metadata[0] context['og_image'] = self.object.og_image or metadata[1] + + # Add submit form context for unified view + # Set default language and theme + if authed: + default_lang = user.profile.language + ace_theme = user.profile.resolved_ace_theme + instance = Submission(user=user.profile, problem=self.object) + else: + # Guest user defaults + default_lang = None + ace_theme = 'twilight' + instance = None # No submission instance for guests + + # Create form + # We pass instance only if authed, or None which implies a new unsaved instance (unbound-ish) + form = ProblemSubmitForm( + instance=instance, + initial={'language': default_lang} if default_lang else {}, + ) + + # Set judge choices if user can edit problem + if authed and self.object.is_editable_by(user): + form.fields['judge'].choices = tuple( + Judge.objects.filter(online=True, problems=self.object).values_list('name', 'name'), + ) + + # Set language queryset + form.fields['language'].queryset = ( + self.object.usable_languages.order_by('name', 'key') + .prefetch_related(Prefetch('runtimeversion_set', RuntimeVersion.objects.order_by('priority'))) + ) + + # If default_lang is None (guest), try to pick the first one as default to avoid "unavailable" warning + if not default_lang and form.fields['language'].queryset.exists(): + default_lang = form.fields['language'].queryset.first() + form.initial['language'] = default_lang + + # Set ACE editor mode and theme + if default_lang: + form.fields['source'].widget.mode = default_lang.ace + form.fields['source'].widget.theme = ace_theme + + context['form'] = form + context['default_lang'] = default_lang + context['no_judges'] = not form.fields['language'].queryset.exists() + context['ACE_URL'] = settings.ACE_URL + + # Add submissions for unified view + if authed: + context['my_submissions'] = Submission.objects.filter( + user=user.profile, + problem=self.object, + ).order_by('-date')[:20] # Limit to 20 for initial view + else: + context['my_submissions'] = [] + return context