import uuid from typing import Any from sqlalchemy.dialects.postgresql import insert from app.db import SessionLocal from app.models import ( Case, CasePhenotype, GenePhenotype, HpoTerm, Job, JobStatus, Prediction, Variant, ) VARIANT_DEFAULTS: dict[str, Any] = { "chrom": "22", "pos": 1, "ref": "A", "alt": "G", "gene": "NF2", "impact": "HIGH", "consequence": "frameshift_variant", "gnomad_af": None, "annotations": {}, } async def seed_case( *, phenotypes: list[tuple[str, str]] | None = None, variants: list[dict[str, Any]] | None = None, gene_terms: dict[str, list[tuple[str, str]]] | None = None, status: JobStatus = JobStatus.succeeded, name: str | None = None, has_frequencies: bool = True, has_effect_scores: bool = True, ) -> tuple[uuid.UUID, uuid.UUID]: """Insert a case, its phenotypes, a job and its variants. Returns (case_id, job_id). `variants` entries override VARIANT_DEFAULTS; a "score" key becomes a Prediction. The job defaults to a run that looked everything up, so a test says so explicitly when it wants the opposite. Every term gets information content 1.0, which makes the phenotype score plain term counting unless a test seeds its own weights. """ async with SessionLocal() as s: case = Case( name=name or f"case-{uuid.uuid4()}", vcf_uri="gs://bucket/proband.vcf.gz", assembly="GRCh38", phenotypes=[CasePhenotype(hpo_id=hpo, label=label) for hpo, label in (phenotypes or [])], ) job = Job( case=case, status=status, vep_version="113.0", has_frequencies=has_frequencies, has_effect_scores=has_effect_scores, ) s.add_all([case, job]) # HPO rows are shared reference data, so two seeds in one test may name the same term. terms_seen: dict[str, str] = dict(phenotypes or []) annotations = [ {"gene_symbol": gene, "hpo_id": hpo, "hpo_name": label} for gene, terms in (gene_terms or {}).items() for hpo, label in terms ] for terms in (gene_terms or {}).values(): terms_seen.update(terms) if annotations: await s.execute(insert(GenePhenotype).values(annotations).on_conflict_do_nothing()) if terms_seen: await s.execute( insert(HpoTerm) .values([{"hpo_id": h, "name": lab, "ic": 1.0} for h, lab in terms_seen.items()]) .on_conflict_do_nothing() ) for spec in variants or []: fields = VARIANT_DEFAULTS | spec score = fields.pop("score", None) variant = Variant(job=job, **fields) s.add(variant) if score is not None: await s.flush() s.add(Prediction(variant_id=variant.id, model_name="rarelens-pathogenicity", model_version="demo", score=float(score))) await s.commit() return case.id, job.id