import uuid from fastapi import APIRouter, Depends, Query from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.db import get_session from app.models import Prediction, Variant from app.schemas import VariantPage router = APIRouter() @router.get("", response_model=VariantPage) async def list_variants( job_id: uuid.UUID, gene: str | None = None, impact: str | None = Query(None, pattern="^(HIGH|MODERATE|LOW|MODIFIER)$"), max_af: float | None = Query(None, ge=0, le=1), min_score: float | None = Query(None, ge=0, le=1), limit: int = Query(50, le=500), offset: int = 0, session: AsyncSession = Depends(get_session), ): stmt = select(Variant).where(Variant.job_id == job_id) if gene: stmt = stmt.where(Variant.gene == gene.upper()) if impact: stmt = stmt.where(Variant.impact == impact) if max_af is not None: stmt = stmt.where((Variant.gnomad_af.is_(None)) | (Variant.gnomad_af <= max_af)) if min_score is not None: stmt = stmt.join(Prediction, Prediction.variant_id == Variant.id).where(Prediction.score >= min_score) total = await session.scalar(select(func.count()).select_from(stmt.subquery())) rows = await session.scalars( stmt.options(selectinload(Variant.prediction)) .order_by(Variant.chrom, Variant.pos) .limit(limit) .offset(offset) ) return VariantPage(items=rows.all(), total=total or 0, limit=limit, offset=offset)