Skip to content

mapped_variants.hgvs_p stores non-protein HGVS for UTR variants #864

Description

@davereinhart

Summary

For variants outside the CDS, MappedVariant.hgvs_p is populated with a nucleotide-style expression carrying a protein accession, such as NP_078951.2:n.*21C>T. This is not valid protein HGVS. The value propagates to the CSV exports (mavedb.post_mapped_hgvs_p) and to the score set view in mavedb-ui, where it renders as a variant label and in histogram tooltips.

Reproduction

curl 'https://reg.genome.network/allele?hgvs=NM_024675.4:c.*21C>T'

The MANE RefSeq transcript allele in the response:

{
  "hgvs": ["NM_024675.4:c.*21C>T"],
  "proteinEffect": { "hgvs": "NP_078951.2:n.*21C>T" },
  "MANE": {
    "nucleotide": { "RefSeq": { "hgvs": "NM_024675.4:c.*21C>T" } },
    "protein":    { "RefSeq": { "hgvs": "NP_078951.2:n.*21C>T" } }
  }
}

ClinGen synthesizes a pseudo-protein expression for positions with no amino-acid consequence: the NP accession paired with the transcript-relative coordinate and an n. prefix. Every UTR variant in an affected score set has this shape.

Root cause

src/mavedb/lib/clingen/allele_registry.py::extract_hgvs_from_ca_allele_data copies ClinGen's value into hgvs_p without checking that it is a p. expression. Both branches are affected:

# transcript_accession branch
if hgvs_c:
    if allele.get("proteinEffect"):
        hgvs_p = allele["proteinEffect"].get("hgvs")
    break

# MANE fallback branch
hgvs_p = allele["MANE"].get("protein", {}).get("RefSeq", {}).get("hgvs")

extract_hgvs_from_pa_allele_data has the same gap, taking aminoAcidAlleles[0]["hgvs"][0] unvalidated.

The value is written to the database by src/mavedb/worker/jobs/external_services/hgvs.py::populate_hgvs_for_score_set:

mapped_variant.hgvs_g = hgvs_g
mapped_variant.hgvs_c = hgvs_c
mapped_variant.hgvs_p = hgvs_p

Suggested fix

src/mavedb/lib/variants.py already defines the needed predicate, is_hgvs_p (re.compile(r"(^|:)p\.")), currently used only as a CSV fallback filter. Apply it at extraction time in all three places:

hgvs_p = allele["proteinEffect"].get("hgvs")
if hgvs_p and not is_hgvs_p(hgvs_p):
    hgvs_p = None

Consider the same treatment for hgvs_c (c./n.) and hgvs_g (g.) so the columns are self-describing by construction.

Follow-up

  1. A backfill is needed for rows already written. A WHERE hgvs_p IS NOT NULL AND hgvs_p NOT LIKE '%:p.%' scan will size the impact.
  2. src/mavedb/lib/csv/specs.py::_post_mapped_hgvs_p returns mapping.hgvs_p unchecked and only validates the VRS fallback path. Consider validating the stored column too, as defense in depth.

Affected surfaces

  • GET /score-sets/{urn}/variants/data and the annotations CSV (mavedb.post_mapped_hgvs_p)
  • v_variant_annotations.hgvs_p
  • mavedb-ui score set view: variant search labels and histogram tooltips

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions