Files
PoleDanceApp/backend/app/routers/registrations.py
Dianaka123 1c5719ac85 Initial commit: Pole Dance Championships App
Full-stack mobile app for pole dance championship management.

Backend: FastAPI + SQLAlchemy 2 (async) + SQLite (dev) / PostgreSQL (prod)
- JWT auth with refresh token rotation
- Championship CRUD with Instagram Graph API sync (APScheduler)
- Registration flow with status management
- Participant list publish with Expo push notifications
- Alembic migrations, pytest test suite

Mobile: React Native + Expo (TypeScript)
- Auth gate: pending approval screen for new members
- Championships list & detail screens
- Registration form with status tracking
- React Query + Zustand + React Navigation v6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 22:47:10 +03:00

124 lines
4.5 KiB
Python

import uuid
from datetime import datetime, timezone
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.crud import crud_championship, crud_registration
from app.database import get_db
from app.dependencies import get_approved_user, get_organizer
from app.models.user import User
from app.schemas.registration import RegistrationCreate, RegistrationOut, RegistrationStatusUpdate
router = APIRouter(prefix="/registrations", tags=["registrations"])
@router.post("", response_model=RegistrationOut, status_code=status.HTTP_201_CREATED)
async def submit_registration(
body: RegistrationCreate,
current_user: User = Depends(get_approved_user),
db: AsyncSession = Depends(get_db),
):
champ = await crud_championship.get(db, body.championship_id)
if not champ:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Championship not found")
if champ.status != "open":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Registration is not open"
)
if champ.registration_close_at and champ.registration_close_at < datetime.now(timezone.utc):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, detail="Registration deadline has passed"
)
existing = await crud_registration.get_by_champ_and_user(
db, body.championship_id, current_user.id
)
if existing:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="You have already registered for this championship",
)
return await crud_registration.create(
db,
championship_id=uuid.UUID(body.championship_id),
user_id=current_user.id,
category=body.category,
level=body.level,
notes=body.notes,
)
@router.get("/my", response_model=list[RegistrationOut])
async def my_registrations(
current_user: User = Depends(get_approved_user),
db: AsyncSession = Depends(get_db),
):
return await crud_registration.list_by_user(db, current_user.id)
@router.get("/{registration_id}", response_model=RegistrationOut)
async def get_registration(
registration_id: str,
current_user: User = Depends(get_approved_user),
db: AsyncSession = Depends(get_db),
):
reg = await crud_registration.get(db, registration_id)
if not reg:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
if str(reg.user_id) != str(current_user.id) and current_user.role not in (
"organizer",
"admin",
):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
return reg
@router.get("/championship/{championship_id}", response_model=list[RegistrationOut])
async def championship_registrations(
championship_id: str,
_organizer: User = Depends(get_organizer),
db: AsyncSession = Depends(get_db),
):
champ = await crud_championship.get(db, championship_id)
if not champ:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
return await crud_registration.list_by_championship(db, championship_id)
@router.patch("/{registration_id}/status", response_model=RegistrationOut)
async def update_registration_status(
registration_id: str,
body: RegistrationStatusUpdate,
_organizer: User = Depends(get_organizer),
db: AsyncSession = Depends(get_db),
):
allowed = {"accepted", "rejected", "waitlisted"}
if body.status not in allowed:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Status must be one of: {', '.join(allowed)}",
)
reg = await crud_registration.get(db, registration_id)
if not reg:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
return await crud_registration.update_status(db, reg, body.status)
@router.delete("/{registration_id}", status_code=status.HTTP_204_NO_CONTENT)
async def withdraw_registration(
registration_id: str,
current_user: User = Depends(get_approved_user),
db: AsyncSession = Depends(get_db),
):
reg = await crud_registration.get(db, registration_id)
if not reg:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
if str(reg.user_id) != str(current_user.id):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
if reg.status != "submitted":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Only submitted registrations can be withdrawn",
)
await crud_registration.delete(db, reg)