# DMOJ: Modern Online Judge DMOJ is a comprehensive open-source online judge platform designed for competitive programming competitions and training. Built with Django, it provides a scalable system for hosting programming contests, managing problem sets, and automatically grading submissions across 60+ programming languages. The platform has been successfully deployed to host thousands of competitions including national olympiads. The system employs a distributed architecture with separate web application and judge servers communicating through a bridge server. Judge servers execute user submissions in sandboxed environments and return grading results through a compressed JSON protocol. The platform supports multiple contest formats (ICPC, IOI, AtCoder, ECOO), real-time submission updates via WebSockets, plagiarism detection through Stanford MOSS, and a sophisticated rating system based on Elo-MMR. Features include organization-based access control, virtual contest participation, multi-language problem statements, automatic PDF generation, and comprehensive admin tools for problem authoring and contest management. ## REST API v2 - Contest List List all contests with filtering capabilities. ```bash # List all contests curl https://dmoj.ca/api/v2/contests # Filter rated contests only curl https://dmoj.ca/api/v2/contests?is_rated=true # Filter by organization curl https://dmoj.ca/api/v2/contests?organization=acm # Response format { "api_version": "2.0", "method": "get", "fetched": "2025-11-28T12:00:00Z", "data": { "current_object_count": 50, "objects": [ { "key": "contest2025", "name": "Annual Programming Contest 2025", "start_time": "2025-12-01T10:00:00Z", "end_time": "2025-12-01T15:00:00Z", "time_limit": null, "is_rated": true, "rate_all": false, "tags": ["algorithms", "data-structures"] } ] } } ``` ## REST API v2 - Contest Details Retrieve comprehensive contest information including problems and rankings. ```bash # Get contest details curl https://dmoj.ca/api/v2/contest/contest2025 # Response includes problems, rankings, format config { "api_version": "2.0", "data": { "key": "contest2025", "name": "Annual Programming Contest 2025", "format": { "name": "ICPC", "config": {"penalty": 20} }, "problems": [ { "points": 100, "partial": true, "is_pretested": false, "problem": { "code": "aplusb", "name": "A Plus B", "points": 5.0 } } ], "rankings": [ { "user": "alice", "score": 500, "cumtime": 3600, "problems": [100, 100, 100, 100, 100] } ] } } ``` ## REST API v2 - Problem List Search and filter problems with full-text search support. ```bash # List problems with full-text search curl https://dmoj.ca/api/v2/problems?search=binary+search # Filter by problem code curl https://dmoj.ca/api/v2/problems?code=aplusb # Filter by group and type curl https://dmoj.ca/api/v2/problems?group=algorithms&type=graph # Filter by organization curl https://dmoj.ca/api/v2/problems?organization=codeforces # Response format { "data": { "objects": [ { "code": "binarysearch", "name": "Binary Search Implementation", "types": ["algorithms", "searching"], "group": "intro", "points": 10.0, "partial": true, "is_public": true, "is_organization_private": false } ] } } ``` ## REST API v2 - Problem Details Get detailed problem information including time/memory limits and language restrictions. ```bash # Get problem details curl https://dmoj.ca/api/v2/problem/aplusb # Response includes limits and allowed languages { "data": { "code": "aplusb", "name": "A Plus B", "authors": ["admin"], "types": ["introductory"], "time_limit": 1.0, "memory_limit": 65536, "points": 5.0, "partial": false, "is_public": true, "allowed_languages": ["PY3", "CPP17", "JAVA"], "language_resource_limits": [ { "language": "PY3", "time_limit": 2.0, "memory_limit": 131072 } ] } } ``` ## REST API v2 - Submission List Query submissions with filtering by user, problem, and status. ```bash # List submissions for a user curl https://dmoj.ca/api/v2/submissions?user=alice # Filter by problem and result curl https://dmoj.ca/api/v2/submissions?problem=aplusb&result=AC # Filter by contest curl https://dmoj.ca/api/v2/submissions?contest=contest2025 # Filter by language curl https://dmoj.ca/api/v2/submissions?language=CPP17 # Response format { "data": { "objects": [ { "id": 123456, "problem": "aplusb", "user": "alice", "date": "2025-11-28T10:30:00Z", "language": "CPP17", "time": 0.12, "memory": 2048, "points": 100.0, "result": "AC" } ] } } ``` ## REST API v2 - Submission Details (Authenticated) View detailed submission results including test case breakdown. ```bash # Get submission details (requires authentication) curl -H "Authorization: Bearer YOUR_TOKEN" \ https://dmoj.ca/api/v2/submission/123456 # Response includes test cases { "data": { "id": 123456, "problem": "aplusb", "user": "alice", "status": "D", "result": "AC", "language": "CPP17", "time": 0.12, "memory": 2048.0, "points": 100.0, "test_cases": [ { "case": 1, "status": "AC", "time": 0.01, "memory": 2048, "points": 10, "total": 10 }, { "case": 2, "status": "AC", "time": 0.02, "memory": 2048, "points": 10, "total": 10 } ] } } ``` ## REST API v2 - User Profile Retrieve user information, solved problems, and contest history. ```bash # Get user profile curl https://dmoj.ca/api/v2/user/alice # Response includes solved problems and rating { "data": { "id": 1, "username": "alice", "points": 1500.0, "performance_points": 2000.0, "problem_count": 150, "solved_problems": ["aplusb", "binarysearch", "sorting"], "organizations": ["acm", "university"], "contests": [ { "key": "contest2025", "score": 500, "cumtime": 3600, "rating": 1850 } ] } } ``` ## Submission Model - Creating and Judging Core model for managing user submissions with automated judging. ```python from judge.models import Submission, Profile, Problem, Language # Create a new submission submission = Submission.objects.create( user=Profile.objects.get(user__username='alice'), problem=Problem.objects.get(code='aplusb'), language=Language.objects.get(key='CPP17') ) # Set source code (one-to-one relation) from judge.models import SubmissionSource SubmissionSource.objects.create( submission=submission, source='#include \nint main() { int a, b; std::cin >> a >> b; std::cout << a + b; }' ) # Submit for judging submission.judge() # Rejudge with audit trail submission.judge(rejudge=True, rejudge_user=request.user) # Abort running submission submission.abort() # Check submission status if submission.status == 'D': # Completed print(f"Result: {submission.result}") # AC, WA, TLE, etc. print(f"Points: {submission.points}") print(f"Time: {submission.time}s") print(f"Memory: {submission.memory}KB") # Check if user can view source code if submission.can_see_detail(request.user): source = submission.source.source ``` ## Problem Model - Access Control and Management Problem entity with comprehensive permission system. ```python from judge.models import Problem, Profile, Language # Get problem with optimizations problem = Problem.objects.select_related('group').get(code='aplusb') # Check permissions user_profile = Profile.objects.get(user__username='alice') if problem.is_accessible_by(request.user): # User can view problem print(f"{problem.name}: {problem.points} points") print(f"Time limit: {problem.time_limit}s") print(f"Memory limit: {problem.memory_limit}KB") if problem.is_editable_by(request.user): # User can edit problem problem.time_limit = 2.0 problem.save() # Language-specific limits from judge.models import LanguageLimit LanguageLimit.objects.create( problem=problem, language=Language.objects.get(key='PY3'), time_limit=3.0, # 3x time for Python memory_limit=131072 # 128MB for Python ) # Update problem statistics problem.update_stats() # Recalculate AC rate, submission counts ``` ## Contest Model - Creating and Managing Contests Contest entity with flexible format system and access control. ```python from judge.models import Contest, ContestProblem, Problem, Profile from django.utils import timezone from datetime import timedelta # Create a new contest contest = Contest.objects.create( key='spring2025', name='Spring Programming Contest 2025', start_time=timezone.now() + timedelta(days=7), end_time=timezone.now() + timedelta(days=7, hours=5), time_limit=None, # No time limit per user is_visible=True, is_rated=True, format_name='icpc', format_config={'penalty': 20}, # 20 min penalty per wrong submission points_precision=0 ) # Add organizers and curators contest.authors.add(Profile.objects.get(user__username='admin')) contest.curators.add(Profile.objects.get(user__username='staff')) # Add problems to contest ContestProblem.objects.create( contest=contest, problem=Problem.objects.get(code='aplusb'), points=100, partial=False, order=1, max_submissions=0 # 0 = unlimited ) # Check access if contest.is_accessible_by(request.user): print("User can view contest") # Restrict to organization from judge.models import Organization contest.organizations.add(Organization.objects.get(slug='acm')) contest.access_code = 'SECRET123' contest.save() # Rate contest after completion contest.rate() # Calculate rating changes ``` ## Contest Participation - Joining and Scoring User participation in contests with automatic score calculation. ```python from judge.models import Contest, ContestParticipation, Profile # User joins contest contest = Contest.objects.get(key='spring2025') user_profile = Profile.objects.get(user__username='alice') participation = ContestParticipation.objects.create( contest=contest, user=user_profile, real_start=timezone.now(), # For time-limited contests virtual=0 # 0 = real, >0 = virtual participation number ) # Submit during contest (automatically linked) from judge.models import Submission, ContestSubmission submission = Submission.objects.create( user=user_profile, problem=Problem.objects.get(code='aplusb'), language=Language.objects.get(key='CPP17'), contest_object=contest ) # Link submission to participation ContestSubmission.objects.create( submission=submission, participation=participation, problem=ContestProblem.objects.get(contest=contest, problem__code='aplusb') ) # Recompute scores after judging participation.recompute_results() # Access computed scores print(f"Score: {participation.score}") print(f"Cumulative time: {participation.cumtime}") print(f"Format data: {participation.format_data}") # Disqualify participant participation.set_disqualified(True) ``` ## Judge API - Submitting to Bridge Server Communication layer between Django site and judge servers. ```python from judge.judgeapi import judge_submission, abort_submission # Submit for judging with priority from judge.models import Submission submission = Submission.objects.get(id=123456) # Normal submission (default priority) success = judge_submission(submission) # Contest submission (higher priority) success = judge_submission(submission) # Auto-detected if in contest # Rejudge submission success = judge_submission(submission, rejudge=True) # Batch rejudge (lower priority) success = judge_submission(submission, batch_rejudge=True) # Force specific judge server success = judge_submission(submission, judge_id='judge1') # Abort running submission abort_submission(submission) # Judge server management from judge.judgeapi import disconnect_judge, update_disable_judge from judge.models import Judge judge = Judge.objects.get(name='judge1') disconnect_judge(judge, force=False) judge.is_disabled = True judge.save() update_disable_judge(judge) ``` ## Judge Handler - Bridge Server Communication Protocol Handler for processing judge server messages via compressed JSON. ```python # Message flow from judge server to Django # Handled by JudgeHandler in judge/bridge/judge_handler.py # 1. Judge connects and handshakes { 'name': 'handshake', 'id': 'judge1', 'key': 'hmac_auth_key', 'executors': {'CPP17': ['G++17'], 'PY3': ['PyPy3']}, 'problems': ['aplusb', 'sorting'] } # 2. Judge acknowledges submission { 'name': 'submission-acknowledged', 'submission-id': 123456 } # 3. Judge starts grading { 'name': 'grading-begin', 'submission-id': 123456, 'is-pretested': False, 'pretests-only': False } # 4. Test case results { 'name': 'test-case-status', 'submission-id': 123456, 'case': 1, 'status': 0, # 0 = AC, 1 = WA, 2 = RTE, etc. 'time': 0.01, 'memory': 2048, 'points': 10.0, 'total-points': 10.0 } # 5. Batch test case (for IOI-style) { 'name': 'batch-begin', 'submission-id': 123456, 'batch-id': 1 } # 6. Grading completion { 'name': 'grading-end', 'submission-id': 123456 } # Final score calculated server-side from test cases ``` ## Contest Format - Custom Scoring Implementation Pluggable contest format system for different competition styles. ```python from judge.contest_format.base import BaseContestFormat from judge.contest_format.registry import register_contest_format @register_contest_format('custom') class CustomContestFormat(BaseContestFormat): name = 'Custom Format' @classmethod def validate(cls, config): # Validate format-specific config if not isinstance(config, dict): raise ValidationError('Config must be a dict') def update_participation(self, participation): # Calculate score and cumtime points = 0 cumtime = 0 format_data = {} # Query submissions for result in participation.submissions.values('problem_id').annotate( time=Max('submission__date'), points=Max('points') ): dt = (result['time'] - participation.start).total_seconds() points += result['points'] cumtime += dt format_data[str(result['problem_id'])] = { 'time': dt, 'points': result['points'] } participation.cumtime = cumtime participation.score = round(points, self.contest.points_precision) participation.tiebreaker = 0 # Custom tiebreaker logic participation.format_data = format_data participation.save() def display_user_problem(self, participation, contest_problem): # Return HTML for scoreboard cell format_data = participation.format_data.get(str(contest_problem.id)) if format_data: return format_html( '{points}', state=self.best_solution_state( format_data['points'], contest_problem.points ), points=format_data['points'] ) return mark_safe('') ``` ## Problem Data - Test Case Configuration Test case and checker configuration for automated grading. ```python from judge.models import ProblemData, ProblemTestCase import zipfile # Create problem data configuration problem_data = ProblemData.objects.create( problem=Problem.objects.get(code='aplusb'), zipfile='aplusb.zip', # Uploaded test data archive checker='standard', # standard, floats, floatsabs, sorted, etc. output_prefix=None, output_limit=131072 # 128KB output limit ) # Add individual test cases ProblemTestCase.objects.create( dataset=problem_data, order=1, type='N', # Normal case input_file='1.in', output_file='1.out', points=10 ) # Batch test cases (for IOI-style subtasks) ProblemTestCase.objects.create( dataset=problem_data, order=2, type='S', # Batch start input_file='', output_file='', points=0 ) ProblemTestCase.objects.create( dataset=problem_data, order=3, type='N', input_file='batch1_1.in', output_file='batch1_1.out', points=20 ) ProblemTestCase.objects.create( dataset=problem_data, order=4, type='E', # Batch end input_file='', output_file='', points=0 ) # Custom checker options problem_data.checker = 'floatsabs' problem_data.checker_args = {'precision': 6} # 6 decimal places problem_data.save() ``` ## Event Poster - Real-time Updates via WebSocket Push real-time updates to connected clients for live submission tracking. ```python from judge import event_poster as event # Post submission update event.post('submissions', { 'type': 'update-submission', 'id': 123456, 'user': 1, 'problem': 'aplusb', 'status': 'G', # Grading 'language': 'CPP17' }) # Post to specific submission channel event.post('sub_123456', { 'type': 'test-case', 'case': 1, 'status': 'AC', 'time': 0.01, 'memory': 2048 }) # Post contest scoreboard update event.post('contest_spring2025', { 'type': 'update-participation', 'participation': 1001, 'score': 500, 'cumtime': 3600 }) # Notify submission completion event.post('sub_123456', { 'type': 'done', 'result': 'AC', 'points': 100.0 }) # Client-side JavaScript subscription # var ws = new WebSocket('ws://dmoj.ca/event/sub_123456'); # ws.onmessage = function(e) { # var data = JSON.parse(e.data); # updateSubmissionStatus(data); # }; ``` ## Organization Model - Access Control Groups Group users for contests and problem access control. ```python from judge.models import Organization, Profile # Create organization org = Organization.objects.create( slug='acm', name='ACM Programming Club', short_name='ACM', about='University ACM programming club', is_open=False, # Requires approval to join slots=100 # Maximum members ) # Add admins org.admins.add(Profile.objects.get(user__username='admin')) # User requests to join from judge.models import OrganizationRequest request = OrganizationRequest.objects.create( organization=org, user=Profile.objects.get(user__username='alice'), reason='I want to participate in ACM contests' ) # Admin approves request request.state = 'A' # Approved request.save() org.members.add(request.user) # Create subclass within organization from judge.models import Class programming_class = Class.objects.create( name='Advanced Algorithms', organization=org ) programming_class.members.add(user_profile) # Restrict contest to organization contest.organizations.add(org) contest.save() ``` ## Summary DMOJ provides a production-ready platform for hosting competitive programming competitions with enterprise-grade features. The REST API v2 enables integration with external systems, mobile applications, and data analysis tools through comprehensive filtering and pagination. Core models (Submission, Problem, Contest, Profile) follow Django best practices with proper foreign key relationships, query optimization, and permission systems. The distributed judging architecture scales horizontally by adding judge servers with intelligent load balancing and priority-based queuing. Real-time WebSocket communication keeps users informed of submission progress without polling. The pluggable contest format system supports ICPC (penalty-based), IOI (subtask scoring), and custom formats through clean inheritance. Integration patterns include OAuth authentication (Google, Facebook, GitHub), Celery for background tasks, caching layers for performance, and event-driven updates for real-time features. The platform is designed for extensibility through settings configuration, custom contest formats, and modular components.