volsu-contests/backend/app/routers/submissions.py
2025-11-30 19:55:50 +03:00

173 lines
5.8 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from app.database import get_db
from app.models.user import User
from app.models.problem import Problem
from app.models.submission import Submission
from app.schemas.submission import SubmissionCreate, SubmissionResponse, SubmissionListResponse
from app.dependencies import get_current_user
from app.services.scoring import evaluate_submission
router = APIRouter()
async def process_submission(submission_id: int, db_url: str):
"""Background task to evaluate submission"""
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy import select
from sqlalchemy.orm import selectinload
engine = create_async_engine(db_url)
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with async_session() as db:
# Get submission with problem and test cases
result = await db.execute(
select(Submission)
.options(selectinload(Submission.problem).selectinload(Problem.test_cases))
.where(Submission.id == submission_id)
)
submission = result.scalar_one_or_none()
if not submission:
return
# Update status to judging
submission.status = "judging"
await db.commit()
try:
# Evaluate submission
result = await evaluate_submission(
source_code=submission.source_code,
language_id=submission.language_id,
test_cases=submission.problem.test_cases,
total_points=submission.problem.total_points,
time_limit_ms=submission.problem.time_limit_ms,
memory_limit_kb=submission.problem.memory_limit_kb,
)
# Update submission with results
submission.status = result["status"]
submission.score = result["score"]
submission.total_points = result["total_points"]
submission.tests_passed = result["tests_passed"]
submission.tests_total = result["tests_total"]
submission.execution_time_ms = result["execution_time_ms"]
submission.memory_used_kb = result["memory_used_kb"]
submission.judge_response = result["details"]
await db.commit()
except Exception as e:
submission.status = "internal_error"
submission.judge_response = {"error": str(e)}
await db.commit()
await engine.dispose()
@router.post("/", response_model=SubmissionResponse, status_code=status.HTTP_201_CREATED)
async def create_submission(
submission_data: SubmissionCreate,
background_tasks: BackgroundTasks,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
# Check if problem exists
result = await db.execute(
select(Problem)
.options(selectinload(Problem.test_cases))
.where(Problem.id == submission_data.problem_id)
)
problem = result.scalar_one_or_none()
if not problem:
raise HTTPException(status_code=404, detail="Problem not found")
# Create submission
submission = Submission(
user_id=current_user.id,
problem_id=submission_data.problem_id,
contest_id=submission_data.contest_id,
source_code=submission_data.source_code,
language_id=submission_data.language_id,
language_name=submission_data.language_name,
status="pending",
total_points=problem.total_points,
tests_total=len(problem.test_cases),
)
db.add(submission)
await db.commit()
await db.refresh(submission)
# Add background task to process submission
from app.config import settings
background_tasks.add_task(process_submission, submission.id, settings.database_url)
return submission
@router.get("/{submission_id}", response_model=SubmissionResponse)
async def get_submission(
submission_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
result = await db.execute(
select(Submission).where(Submission.id == submission_id)
)
submission = result.scalar_one_or_none()
if not submission:
raise HTTPException(status_code=404, detail="Submission not found")
# Users can only see their own submissions
if submission.user_id != current_user.id and current_user.role != "admin":
raise HTTPException(status_code=403, detail="Access denied")
return submission
@router.get("/", response_model=list[SubmissionListResponse])
async def get_my_submissions(
problem_id: int | None = None,
contest_id: int | None = None,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
query = select(Submission).where(Submission.user_id == current_user.id)
if problem_id:
query = query.where(Submission.problem_id == problem_id)
if contest_id:
query = query.where(Submission.contest_id == contest_id)
query = query.order_by(Submission.created_at.desc())
result = await db.execute(query)
return result.scalars().all()
@router.get("/problem/{problem_id}", response_model=list[SubmissionListResponse])
async def get_submissions_by_problem(
problem_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
# Users can only see their own submissions
result = await db.execute(
select(Submission)
.where(
Submission.problem_id == problem_id,
Submission.user_id == current_user.id,
)
.order_by(Submission.created_at.desc())
)
return result.scalars().all()