import uuid from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.db import get_session from app.models import Job, Sample from app.schemas import JobOut, SampleCreate, SampleOut from app.services.events import publish_vcf_uploaded router = APIRouter() @router.get("", response_model=list[SampleOut]) async def list_samples(session: AsyncSession = Depends(get_session)): result = await session.scalars(select(Sample).order_by(Sample.created_at.desc())) return result.all() @router.post("", response_model=SampleOut, status_code=status.HTTP_201_CREATED) async def create_sample(payload: SampleCreate, session: AsyncSession = Depends(get_session)): sample = Sample(**payload.model_dump()) session.add(sample) await session.commit() await session.refresh(sample) return sample @router.get("/{sample_id}/jobs", response_model=list[JobOut]) async def list_jobs(sample_id: uuid.UUID, session: AsyncSession = Depends(get_session)): result = await session.scalars( select(Job).where(Job.sample_id == sample_id).order_by(Job.created_at.desc()) ) return result.all() @router.post("/{sample_id}/annotate", response_model=JobOut, status_code=status.HTTP_202_ACCEPTED) async def annotate(sample_id: uuid.UUID, session: AsyncSession = Depends(get_session)): sample = await session.get(Sample, sample_id) if sample is None: raise HTTPException(404, "sample not found") job = Job(sample_id=sample.id) session.add(job) await session.commit() await session.refresh(job) # Emits to Pub/Sub in GCP; runs the Nextflow pipeline inline for local dev. await publish_vcf_uploaded(job_id=job.id, vcf_uri=sample.vcf_uri) return job