173 lines
5.8 KiB
Python
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()
|