178 lines
5.3 KiB
Python
178 lines
5.3 KiB
Python
from typing import List, Optional
|
|
from uuid import UUID
|
|
from django.http import HttpRequest
|
|
from ninja import Router, File, Form
|
|
from ninja.files import UploadedFile
|
|
from django.shortcuts import get_object_or_404
|
|
|
|
from games.models import Game
|
|
from .models import Submission, SubmissionFile, ImageCrop, OCRStatus, Scope
|
|
from .schemas import SubmissionOut, ImageCropIn, ImageCropOut, ScopeOut
|
|
from .tasks import process_submission_file, process_crop
|
|
|
|
router = Router()
|
|
|
|
PREFIX = "/events/{event_path}/submissions"
|
|
|
|
|
|
@router.get(f"{PREFIX}/scopes/mine", response={200: list[ScopeOut], 403: dict})
|
|
def list_my_scopes(request: HttpRequest, event_path: str, game_path: str):
|
|
if not request.user.is_authenticated:
|
|
return 403, {"detail": "Authentication required"}
|
|
|
|
game = get_object_or_404(
|
|
Game.objects.enabled_for_event(request.event), path=game_path
|
|
)
|
|
|
|
return Scope.objects.filter(
|
|
event_game__event=request.event,
|
|
event_game__game=game,
|
|
assigned_users=request.user,
|
|
)
|
|
|
|
|
|
@router.post(f"{PREFIX}/submission/create", response={200: SubmissionOut, 403: dict})
|
|
def create_submission(
|
|
request: HttpRequest,
|
|
event_path: str,
|
|
scope_id: UUID,
|
|
files: List[UploadedFile] = File(...),
|
|
notes: Optional[str] = Form(None),
|
|
):
|
|
if not request.user.is_authenticated:
|
|
return 403, {"detail": "Authentication required"}
|
|
|
|
scope = get_object_or_404(
|
|
Scope, id=scope_id, event_game__event=request.event, is_active=True
|
|
)
|
|
|
|
if (
|
|
not request.user.is_staff
|
|
and not scope.assigned_users.filter(pk=request.user.pk).exists()
|
|
):
|
|
return 403, {"detail": "You are not assigned to this scope"}
|
|
|
|
submission = Submission.objects.create(
|
|
event=request.event,
|
|
scope=scope,
|
|
user=request.user,
|
|
notes=notes or None,
|
|
)
|
|
|
|
for file in files:
|
|
SubmissionFile.objects.create(
|
|
submission=submission,
|
|
file=file,
|
|
original_filename=file.name,
|
|
content_type=file.content_type or "application/octet-stream",
|
|
)
|
|
|
|
return submission
|
|
|
|
|
|
@router.get(
|
|
f"{PREFIX}/submission/by/user", response={200: list[SubmissionOut], 403: dict}
|
|
)
|
|
def list_user_submissions(request: HttpRequest, event_path: str, scope_id: UUID):
|
|
if not request.user.is_authenticated:
|
|
return 403, {"detail": "Authentication required"}
|
|
|
|
submissions = Submission.for_event.filter(
|
|
scope_id=scope_id, user=request.user
|
|
).order_by("-created_at")
|
|
return submissions
|
|
|
|
|
|
@router.get(f"{PREFIX}/submission/by/game", response=list[SubmissionOut])
|
|
def list_submissions_by_game(request: HttpRequest, event_path: str, game_path: str):
|
|
game = get_object_or_404(Game, path=game_path)
|
|
|
|
if not request.user.is_staff:
|
|
return 403, {"detail": "Admin access required"}
|
|
|
|
return Submission.for_event.filter(scope__event_game__game=game)
|
|
|
|
|
|
@router.get(f"{PREFIX}/submission/{{submission_id}}", response=SubmissionOut)
|
|
def get_submission(request: HttpRequest, event_path: str, submission_id: UUID):
|
|
submission = get_object_or_404(Submission.for_event, id=submission_id)
|
|
|
|
if submission.user != request.user and not request.user.is_staff:
|
|
return 403, {"detail": "Not authorized"}
|
|
|
|
return submission
|
|
|
|
|
|
@router.post(f"{PREFIX}/crops/s/{{submission_file_id}}", response=ImageCropOut)
|
|
def create_image_crop(
|
|
request: HttpRequest,
|
|
event_path: str,
|
|
submission_file_id: UUID,
|
|
data: ImageCropIn,
|
|
):
|
|
if not request.user.is_staff:
|
|
return 403, {"detail": "Admin access required"}
|
|
|
|
submission_file = get_object_or_404(
|
|
SubmissionFile, id=submission_file_id, submission__event=request.event
|
|
)
|
|
|
|
crop = ImageCrop.objects.create(
|
|
submission_file=submission_file,
|
|
x1=data.x1,
|
|
y1=data.y1,
|
|
x2=data.x2,
|
|
y2=data.y2,
|
|
label=data.label or "",
|
|
ocr_status=OCRStatus.TO_PROCESS,
|
|
)
|
|
|
|
return crop
|
|
|
|
|
|
@router.delete(f"{PREFIX}/crops/{{crop_id}}", response=dict)
|
|
def delete_image_crop(request: HttpRequest, event_path: str, crop_id: UUID):
|
|
if not request.user.is_staff:
|
|
return 403, {"detail": "Admin access required"}
|
|
|
|
crop = get_object_or_404(
|
|
ImageCrop, id=crop_id, submission_file__submission__event=request.event
|
|
)
|
|
crop.delete()
|
|
|
|
return {"status": "deleted"}
|
|
|
|
|
|
@router.post(f"{PREFIX}/files/{{file_id}}/process-ocr", response=dict)
|
|
def process_file_ocr(request: HttpRequest, event_path: str, file_id: UUID):
|
|
if not request.user.is_staff:
|
|
return 403, {"detail": "Admin access required"}
|
|
|
|
submission_file = get_object_or_404(
|
|
SubmissionFile, id=file_id, submission__event=request.event
|
|
)
|
|
submission_file.ocr_status = OCRStatus.TO_PROCESS
|
|
submission_file.save()
|
|
|
|
# Enqueue the OCR processing task
|
|
process_submission_file.enqueue(str(file_id))
|
|
|
|
return {"status": "queued"}
|
|
|
|
|
|
@router.post(f"{PREFIX}/crops/{{crop_id}}/process-ocr", response=dict)
|
|
def process_crop_ocr(request: HttpRequest, event_path: str, crop_id: UUID):
|
|
if not request.user.is_staff:
|
|
return 403, {"detail": "Admin access required"}
|
|
|
|
crop = get_object_or_404(
|
|
ImageCrop, id=crop_id, submission_file__submission__event=request.event
|
|
)
|
|
crop.ocr_status = OCRStatus.TO_PROCESS
|
|
crop.save()
|
|
|
|
# Enqueue the OCR processing task
|
|
process_crop.enqueue(str(crop_id))
|
|
|
|
return {"status": "queued"}
|