Skip to content

feat: Add dedicated table for VEP annotations #772

Description

@bencap

Context

Depends on: #739

VEP results currently live as two columns on mapped_variants (vep_functional_consequence, vep_access_date), which holds a single consequence string with no record of how it was derived. VEPAnnotation replaces them with a temporal per-allele table.

Schema

Column Notes
allele_id FK to alleles
consequence_terms all terms from the matched transcript entry
most_severe_consequence single term, ranked by Ensembl severity order
consequence_source enum: transcript | most_severe
matched_transcript transcript the consequence was read from; null when consequence_source = 'most_severe'
access_date date of the VEP call
source_version version the VEP call was made against
error sanitized failure description; null on success
created_at / superseded_at temporal support

Consequence resolution

VEP's top-level most_severe_consequence is the worst call across every transcript overlapping the variant's position. Overlapping transcripts have different exon structures and reading frames, so that field regularly reports a consequence that does not apply to the transcript the allele belongs to. A single BRCA1 coding variant returns 368 transcript_consequences entries.

Resolution is against Allele.transcript, which is NOT NULL at every level:

  1. Send refseq=1 for NM_/NR_ transcripts so transcript_consequences carries RefSeq IDs. Ensembl and genomic HGVS are sent without it.
  2. Match Allele.transcript against transcript_consequences[].transcript_id, comparing with versions stripped.
  3. On a match, take the most severe of that entry's consequence_terms and record consequence_source = 'transcript' with matched_transcript set.
  4. With no match, use the top-level most_severe_consequence and record consequence_source = 'most_severe'.

Severity ranking uses Ensembl's published order (rest.ensembl.org/info/variation/consequence_types?rank=1, 41 terms). Terms outside that list are not ranked; the first term is used rather than dropping the consequence.

Routing

Only level = 'coding' and level = 'genomic' alleles are annotated. Protein-level alleles are never sent to VEP and the coding alleles enumerated by reverse translation carry the answer instead. In the UI, a protein level allele could aggregate over the most severe consequence of the underlying VEP calls of its encoding variants.

Acceptance Criteria

  • VEPAnnotation table created with the schema above; vep_functional_consequence and vep_access_date removed from mapped_variants
  • Consequence is read from the transcript_consequences entry matching Allele.transcript; most_severe_consequence is used only when no entry matches
  • consequence_source and matched_transcript are populated on every row
  • refseq=1 is sent for NM_/NR_ transcripts and omitted otherwise
  • Alleles with level = 'protein' are not submitted to VEP
  • Writing a new annotation sets superseded_at on the previous current row in the same transaction
  • Temporal query at a past timestamp returns exactly one row per allele

Metadata

Metadata

Assignees

Labels

app: backendTask implementation touches the backendapp: databaseTask implementation requires database changes

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions