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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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