Analysis: dedicated Structural Variant node for AnnotSV columns
- Dominant language
- Python
- Stars
- 30
- Forks
- 3
- Avg merge
- 9h 28m
- Merged PRs (30d)
- 42
Description
🤖 Written by Claude
The 25 AnnotSV `VariantGridColumn`s added in #1533 (registered in `snpdb/migrations/0180_new_annotsv_variantgrid_columns.py`, mapped in `annotation/annotsv_columns.py`) are `queryset_field=True`, so they already work in `FilterNode` and as grid columns. That generic path isn't good enough though, and we should add a dedicated SV filter node.
### Why not just use FilterNode
- **Everything is NULL for small variants.** AnnotSV only runs over SVs, so in a typical analysis 99%+ of rows have every `annotsv_*` field null. A hand-built rule like `annotsv_acmg_class >= 4` silently deletes the entire small-variant population — the footgun `DamageNode`'s `_required`/`_allow_null` triplets exist to manage.
- **The left/right pairs need collapsing.** "Exclude SVs with a breakpoint in a blacklist/segdup/repeat region" is 6 columns nobody wires up by hand correctly.
- **The four `B_*_AFmax` are mutually exclusive per variant** — only the one matching the SV type is populated. The correct rare-SV filter is `AND over the four of (isnull OR <= x)`. An OR of `lte` passes anything with three nulls.
### Why a dedicated node rather than spreading them
`PopulationNode` would need a second SV-aware null path through an already dense node. `annotsv_acmg_class` is a classification, not a per-tool score, so it doesn't belong in `DamageNode`'s OR scoring pool. A dedicated node scopes itself to SVs, and everything inside then ANDs cleanly with no per-field `allow_null`.
### Sketch — `SVNode` ("Structural Variant", `analysis/models/nodes/filters/sv_node.py`)
**Scope** — `structural_variants_only` (default True) → `Q(svlen__isnull=False)`, or `variant_class__in` the SV entries of `VARIANT_CLASS_GROUPS`. Plus `sv_size_min`/`sv_size_max` on `abs(svlen)`, which is available nowhere today.
**ACMG** — `acmg_class_min` dropdown off `VariantAnnotation.ANNOTSV_ACMG_CLASS_CLINICAL_SIGNIFICANCE`, same shape as `BuiltInFilterNode.clinvar_stars_min`; `acmg_score_min` for the raw ranking score. This is the headline filter.
**Frequency** — one `benign_sv_percent` slider (matching `PopulationNode.percent`), applied as `AND over the 4 B_*_AFmax of (isnull OR __lte)`, plus `allow_null`.
**Artefact regions** — three checkboxes over six columns: `exclude_encode_blacklist`, `exclude_segdup`, `exclude_repeat`, each `~(Q(left__isnull=False) | Q(right__isnull=False))`.
**Gene impact** — `frameshift`, `exons_spanned_min`, `dist_nearest_ss_max`, `omim_morbid`, `has_re_gene`, `has_pathogenic_overlap` (`annotsv_pathogenic_overlaps__isnull=False`). Leave the nested JSON structure alone for v1.
**Left in FilterNode/grid** — `acmg_criteria`, `nearest_ss_type`, `encode_blacklist_characteristics_*`, `omim_phenotype`/`omim_id`, `re_gene` names. Free text, already work, don't earn a widget.
### Also wire in
- `_get_node_extra_columns()` — push the annotsv columns the node filtered on into the downstream grid, the way `CohortNode` does with its count columns. Today you can filter on ACMG class and then not see it unless you're on "All columns".
- `get_warnings()` — call `AnnotationPipelineVersion.get_active(VariantAnnotationPipelineType.ANNOTSV, genome_build)`; if `None`, AnnotSV was never registered for this build and the node filters everything out. Same pattern as the `backfilled_*` warnings in `DamageNode`/`ConservationNode`.
### Intended usage
`SampleNode` → fork: SV branch through `SVNode`, small-variant branch through `Damage`/`Population` → `MergeNode`. The `structural_variants_only` scope is what makes that fork safe without the user setting `variant_class` on both sides.
Related: #1533, #720
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with analysis/models/nodes/filters/sv_node.py, annotation/annotsv_columns.py, and the existing DamageNode, ConservationNode, BuiltInFilterNode, PopulationNode, and CohortNode patterns. Done means a dedicated SVNode supports the specified scope, ACMG, frequency, artefact, and gene-impact filters, exposes filtered columns through _get_node_extra_columns(), and warns when AnnotSV is unavailable for the genome build.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100