Moved to S3
This commit is contained in:
@@ -3,6 +3,7 @@ Assignment details and dispute system endpoints.
|
||||
"""
|
||||
from datetime import datetime, timedelta
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import Response
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
@@ -17,6 +18,7 @@ from app.schemas import (
|
||||
MessageResponse, ChallengeResponse, GameShort, ReturnedAssignmentResponse,
|
||||
)
|
||||
from app.schemas.user import UserPublic
|
||||
from app.services.storage import storage_service
|
||||
|
||||
router = APIRouter(tags=["assignments"])
|
||||
|
||||
@@ -133,10 +135,7 @@ async def get_assignment_detail(
|
||||
can_dispute = time_since_completion < timedelta(hours=DISPUTE_WINDOW_HOURS)
|
||||
|
||||
# Build proof URLs
|
||||
proof_image_url = None
|
||||
if assignment.proof_path:
|
||||
# Extract filename from path
|
||||
proof_image_url = f"/uploads/proofs/{assignment.proof_path.split('/')[-1]}"
|
||||
proof_image_url = storage_service.get_url(assignment.proof_path, "proofs")
|
||||
|
||||
return AssignmentDetailResponse(
|
||||
id=assignment.id,
|
||||
@@ -153,7 +152,7 @@ async def get_assignment_detail(
|
||||
game=GameShort(
|
||||
id=game.id,
|
||||
title=game.title,
|
||||
cover_url=f"/uploads/covers/{game.cover_path.split('/')[-1]}" if game.cover_path else None,
|
||||
cover_url=storage_service.get_url(game.cover_path, "covers"),
|
||||
),
|
||||
is_generated=challenge.is_generated,
|
||||
created_at=challenge.created_at,
|
||||
@@ -172,6 +171,58 @@ async def get_assignment_detail(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/assignments/{assignment_id}/proof-image")
|
||||
async def get_assignment_proof_image(
|
||||
assignment_id: int,
|
||||
current_user: CurrentUser,
|
||||
db: DbSession,
|
||||
):
|
||||
"""Stream the proof image for an assignment"""
|
||||
# Get assignment
|
||||
result = await db.execute(
|
||||
select(Assignment)
|
||||
.options(
|
||||
selectinload(Assignment.challenge).selectinload(Challenge.game),
|
||||
)
|
||||
.where(Assignment.id == assignment_id)
|
||||
)
|
||||
assignment = result.scalar_one_or_none()
|
||||
|
||||
if not assignment:
|
||||
raise HTTPException(status_code=404, detail="Assignment not found")
|
||||
|
||||
# Check user is participant of the marathon
|
||||
marathon_id = assignment.challenge.game.marathon_id
|
||||
result = await db.execute(
|
||||
select(Participant).where(
|
||||
Participant.user_id == current_user.id,
|
||||
Participant.marathon_id == marathon_id,
|
||||
)
|
||||
)
|
||||
participant = result.scalar_one_or_none()
|
||||
if not participant:
|
||||
raise HTTPException(status_code=403, detail="You are not a participant of this marathon")
|
||||
|
||||
# Check if proof exists
|
||||
if not assignment.proof_path:
|
||||
raise HTTPException(status_code=404, detail="No proof image for this assignment")
|
||||
|
||||
# Get file from storage
|
||||
file_data = await storage_service.get_file(assignment.proof_path, "proofs")
|
||||
if not file_data:
|
||||
raise HTTPException(status_code=404, detail="Proof image not found in storage")
|
||||
|
||||
content, content_type = file_data
|
||||
|
||||
return Response(
|
||||
content=content,
|
||||
media_type=content_type,
|
||||
headers={
|
||||
"Cache-Control": "public, max-age=31536000",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.post("/assignments/{assignment_id}/dispute", response_model=DisputeResponse)
|
||||
async def create_dispute(
|
||||
assignment_id: int,
|
||||
@@ -421,7 +472,7 @@ async def get_returned_assignments(
|
||||
game=GameShort(
|
||||
id=a.challenge.game.id,
|
||||
title=a.challenge.game.title,
|
||||
cover_url=f"/uploads/covers/{a.challenge.game.cover_path.split('/')[-1]}" if a.challenge.game.cover_path else None,
|
||||
cover_url=storage_service.get_url(a.challenge.game.cover_path, "covers"),
|
||||
),
|
||||
is_generated=a.challenge.is_generated,
|
||||
created_at=a.challenge.created_at,
|
||||
|
||||
Reference in New Issue
Block a user