Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions VariantValidator/modules/gapped_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -1864,6 +1864,9 @@ def g_to_t_compensation(self, ori, hgvs_coding, rec_var):
hgvs_genomic.posedit.pos.end.base = start
hgvs_genomic = self.variant.hn.normalize(hgvs_genomic)

logger.info(f"g_to_t_compensation returning hgvs_genomic {hgvs_genomic }hgvs_coding {hgvs_coding}, "
f"suppress c normalization {suppress_c_normalization}")

return hgvs_genomic, suppress_c_normalization, hgvs_coding

def g_to_t_gapped_mapping_stage2(self, ori, hgvs_coding, hgvs_genomic):
Expand Down Expand Up @@ -2002,14 +2005,17 @@ def g_to_t_gapped_mapping_stage2(self, ori, hgvs_coding, hgvs_genomic):
else:
hgvs_coding = copy.deepcopy(hgvs_refreshed_variant)

logger.info(f"g_to_t_gaped_mapping_stage2 returning hgvs_coding {hgvs_coding}")
return hgvs_coding

def g_to_t_gap_compensation_version3(self, hgvs_alt_genomic, hgvs_coding, ori, alt_chr, rec_var):

self.orientation = int(ori[0]['alt_strand'])
hgvs_genomic = copy.deepcopy(hgvs_alt_genomic)

logger.debug('g_to_t gap code 3 active')
logger.debug(f"g_to_t_gap_compensation_version3 triggered with hgvs_alt_genomic {hgvs_alt_genomic},"
f" hgvs_coding {hgvs_coding}, alt_chr {alt_chr}, rec_var {rec_var}, ori {ori}")

rn_hgvs_genomic = self.variant.reverse_normalizer.normalize(hgvs_alt_genomic)
self.hgvs_genomic_possibilities.append([rn_hgvs_genomic, ['false', 'false']])
if self.orientation != -1:
Expand Down Expand Up @@ -2712,6 +2718,8 @@ def g_to_t_gap_compensation_version3(self, hgvs_alt_genomic, hgvs_coding, ori, a
except UnboundLocalError:
pass

logger.debug(f"g_to_t_gap_compensation_version3 returning hgvs_genomic {hgvs_genomic}, "
f"hgvs_coding {hgvs_coding}")
return hgvs_alt_genomic, hgvs_coding

def dup_ins_5prime_shift(self, stored_hgvs_not_delins, saved_hgvs_coding):
Expand Down Expand Up @@ -2984,8 +2992,11 @@ def transcript_disparity(self, reverse_normalized_hgvs_genomic, stored_hgvs_not_
c_tx_hgvs_not_delins = self.validator.vm.n_to_c(self.tx_hgvs_not_delins)
except Exception:
c_tx_hgvs_not_delins = copy.copy(self.tx_hgvs_not_delins)
genomic_gap_fill_variant_alt = self.validator.vm.t_to_g(c_tx_hgvs_not_delins, self.hgvs_genomic_5pr.ac,
alt_aln_method=self.validator.alt_aln_method)
genomic_gap_fill_variant_alt = self.validator.myvm_t_to_g(c_tx_hgvs_not_delins,
self.hgvs_genomic_5pr.ac,
self.variant.no_norm_evm,
self.variant.hn,
self.variant.map_dat)

# Ensure an ALT exists
try:
Expand Down
476 changes: 295 additions & 181 deletions VariantValidator/modules/gene2transcripts.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions VariantValidator/modules/mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,12 +1040,12 @@ def final_tx_to_multiple_genomic(variant, validator, tx_variant, liftover_level=
# Loop out gap code under these circumstances!
if variant.map_dat.is_gapped_map(variant.hgvs_coding.ac,hgvs_alt_genomic.ac,validator):
# warn on gap_compensation for
logger.debug("gap_compensation_3 done for %s when mapped to %s" %
(variant.hgvs_coding.ac, hgvs_alt_genomic.ac))
gap_mapper = gapped_mapping.GapMapper(variant, validator)
hgvs_alt_genomic, hgvs_coding = gap_mapper.g_to_t_gap_compensation_version3(
hgvs_alt_genomic, variant.hgvs_coding, ori, alt_chr, rec_var)
logger.info(f"gap_compensation_3 done for {variant.hgvs_coding} mapped to {hgvs_alt_genomic}")
variant.hgvs_coding = hgvs_coding
logger.info(f"hgvs_coding updated to {hgvs_coding}")

# Check for mismatched sequence in dup variants
if hgvs_alt_genomic.posedit.edit.type == hgvs_coding.posedit.edit.type and \
Expand Down
17 changes: 17 additions & 0 deletions VariantValidator/modules/transcript_map_data.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import copy
import logging

logger = logging.getLogger(__name__)

"""
A module for holding the TranscriptMapData specific to an individual transcript
in order to avoid having to repeatedly re-pull from the database.
Expand All @@ -22,6 +26,9 @@ def __init__(self,hdp=None):
self.mapped_strands = {} # made using above map data, for dict fetch
self.mapping_types = {} # the mapping type for each tx->alt map
self.exon_data = {} # the set of exon data per mapping
self._known_bad_alignments = [
["NM_001009944.3", "NT_187607.1"]
]

def mapping_options(self,tx_ac,hdp=None):
"""
Expand All @@ -45,6 +52,14 @@ def mapping_options(self,tx_ac,hdp=None):
"provider (hdp) for use as a data source")
self.mapping_opts[tx_ac] = cur_hdp.get_tx_mapping_options(
tx_ac,gap_warn=True)

self.mapping_opts[tx_ac] = [
option
for option in self.mapping_opts[tx_ac]
if [option[0], option[1]] not in self._known_bad_alignments
]

logger.info(f"Mapping options for {tx_ac}: {self.mapping_opts[tx_ac]}")
return self.mapping_opts[tx_ac]

def map_strand(self,tx_ac,alt_ac,hdp=None):
Expand Down Expand Up @@ -157,7 +172,9 @@ def tx_exons(self, tx_ac, alt_ac, alt_aln_method, hdp=None):
# If on the reverse strand, reverse the order of elements
if tx_exons[0]['alt_strand'] == -1:
tx_exons = tx_exons[::-1]
logger.debug(f"Exon data for {tx_ac}: {tx_exons}")
return tx_exons
else:
logger.debug(f"Exon data for {tx_ac}: {tx_exons}")
return tx_exons

1 change: 1 addition & 0 deletions VariantValidator/modules/variant.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def __init__(self, original, quibble=None, warnings=None, write=True, primary_as
self.input_parses = None # quibble as hgvs variant object
self.transcript_type = None
self.lovd_syntax_check = None
self.shorthand_vcf = None
self.lovd_messages = None
self.lovd_corrections = None

Expand Down
11 changes: 10 additions & 1 deletion VariantValidator/modules/vvMixinConverters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1096,7 +1096,7 @@ def myvm_t_to_g(self, hgvs_c, alt_chr, no_norm_evm, hn, map_dat):
hgvs_genomic.posedit.edit.alt = hgvs_genomic.posedit.edit.ref
if hgvs_genomic.posedit.edit.type == 'ins' and utilise_gap_code is True:
try:
pre_norm_genomic = copy.copy(hgvs_genomic)# can move ins variants (and in doing so break mid base == original bases assumption)
pre_norm_genomic = copy.copy(hgvs_genomic) # can move ins variants (and in doing so break mid base == original bases assumption)
hgvs_genomic = hn.normalize(hgvs_genomic)
if stored_hgvs_c.posedit.edit.alt and len(stored_hgvs_c.posedit.edit.alt) + 2 == \
len(hgvs_c.posedit.edit.alt) and hgvs_c.posedit.edit.alt == pre_norm_genomic.posedit.edit.alt:
Expand All @@ -1119,6 +1119,14 @@ def myvm_t_to_g(self, hgvs_c, alt_chr, no_norm_evm, hn, map_dat):
hgvs_genomic.posedit.pos.end.base = start
hgvs_genomic = hn.normalize(hgvs_genomic)

except AttributeError as e:
if "'Dup' object has no attribute 'alt'" in str(e):
logger.error(
f"Code triggered previously in very poor alignment so not able to fully test, refer to "
f"test_inputs.py tests test_alt_gapping_bug: "
f"hgvs_genomic: {hgvs_genomic}, stored_hgvs_c: {stored_hgvs_c}")
raise

# Statements required to reformat the stored_hgvs_c into a useable synonym
if (stored_hgvs_c.posedit.edit.ref == '' or stored_hgvs_c.posedit.edit.ref is None) and expand_out:
if stored_hgvs_c.type == 'c':
Expand Down Expand Up @@ -1396,6 +1404,7 @@ def myvm_t_to_g(self, hgvs_c, alt_chr, no_norm_evm, hn, map_dat):
hgvs_genomic = hn.normalize(hgvs_genomic)
except:
pass

# Correct expansion ref + 2
elif expand_out and (
len(hgvs_genomic.posedit.edit.ref) == (len(stored_hgvs_c.posedit.edit.ref) + 2)): # >= 3:
Expand Down
11 changes: 9 additions & 2 deletions VariantValidator/modules/vvMixinCore.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ def validate(self,
select_transcripts,
transcript_set=None,
liftover_level=False,
lovd_syntax_check=False):
lovd_syntax_check=False,
shorthand_vcf=False):
"""
This is the main validator function.
:param batch_variant: A string containing the variant to be validated
Expand All @@ -59,6 +60,7 @@ def validate(self,
Selecting multiple transcripts will lead to a multiple variant outputs.
:param transcript_set: 'refseq' or 'ensembl'
:param lovd_syntax_check: True or False
:param shorthand_vcf: True or False
:return:
"""
logger.debug("Running validate with inputs %s and assembly %s", batch_variant, selected_assembly)
Expand All @@ -78,6 +80,9 @@ def validate(self,
self.selected_assembly = selected_assembly
self.select_transcripts = select_transcripts

# Set output VCF format
self.shorthand_vcf = shorthand_vcf

# Set LOVD syntax checker
self.lovd_syntax_check = lovd_syntax_check

Expand Down Expand Up @@ -1607,11 +1612,13 @@ def _apply_met_variation(data):
else:
variant.hgvs_refseqgene_variant = ''
hgd = "hgvs_genomic_description"
def _vcf_abrv(hgvs,vcf,max_non_abrv_len=100):
def _vcf_abrv(hgvs,vcf,max_non_abrv_len=500):
"""
Abbreviate long del/dup/ins type vcf
Use start-stop as pos ref N alt as type,
not pos as start ref and alt as (extra long seq"""
if self.shorthand_vcf is False:
return vcf
try:
# only shorten vcf if ref based and long
# (and not uncertain which should not have vcf data)
Expand Down
5 changes: 5 additions & 0 deletions tests/test_gene2transcript.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,18 @@ def test_select_transcripts(self):
output_d = self.vv.gene2transcripts('BRCA2', select_transcripts='flibble')
output_e = self.vv.gene2transcripts('BRAF', select_transcripts='mane_select')
output_f = self.vv.gene2transcripts('BRAF', select_transcripts='mane')
output_g = self.vv.gene2transcripts('BRCA2', select_transcripts='all')
output_h = self.vv.gene2transcripts('BRCA2', select_transcripts='raw')

print(output)
assert len(output['transcripts']) >= 3
assert len(output_b['transcripts']) == 1
assert len(output_c['transcripts']) == 2
assert len(output_d['transcripts']) == 0
assert len(output_e['transcripts']) == 1
assert len(output_f['transcripts']) >= 2
assert (len(output_b['transcripts']) < len(output_c['transcripts']) < len(output_g['transcripts'])
< len(output_h['transcripts']))

def test_symbol_valid_hgnc_id(self):
symbol = 'HGNC:2197'
Expand Down
65 changes: 51 additions & 14 deletions tests/test_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1159,7 +1159,7 @@ def test_variant20(self):

def test_variant21(self):
variant = 'NM_000518.4:c.316_*100del'
results = self.vv.validate(variant, 'GRCh37', 'all').format_as_dict(test=True)
results = self.vv.validate(variant, 'GRCh37', 'all', shorthand_vcf=True).format_as_dict(test=True)
print(results)

assert results['flag'] == 'gene_variant'
Expand All @@ -1178,13 +1178,37 @@ def test_variant21(self):
assert results['NM_000518.4:c.316_*100del']['hgvs_lrg_variant'] == ''
self.assertCountEqual(results['NM_000518.4:c.316_*100del']['alt_genomic_loci'], [])
assert results['NM_000518.4:c.316_*100del']['primary_assembly_loci']['hg19'] == {
'hgvs_genomic_description': 'NC_000011.9:g.5246728_5246956del', 'vcf': {'alt': 'DEL', 'chr': 'chr11', 'pos': '5246728-5246956', 'ref': 'N'}}
assert results['NM_000518.4:c.316_*100del']['primary_assembly_loci']['hg38'] == {
'hgvs_genomic_description': 'NC_000011.10:g.5225498_5225726del', 'vcf': {'alt': 'DEL', 'chr': 'chr11', 'pos': '5225498-5225726', 'ref': 'N'}}
assert results['NM_000518.4:c.316_*100del']['primary_assembly_loci']['grch37'] == {
'hgvs_genomic_description': 'NC_000011.9:g.5246728_5246956del', 'vcf': {'alt': 'DEL', 'chr': '11', 'pos': '5246728-5246956', 'ref': 'N'}}
"hgvs_genomic_description": "NC_000011.9:g.5246728_5246956del",
"vcf": {
"alt": "A",
"chr": "chr11",
"pos": "5246727",
"ref": "AATCCAGATGCTCAAGGCCCTTCATAATATCCCCCAGTTTAGTAGTTGGACTTAGGGAACAAAGGAACCTTTAATAGAAATTGGACAGCAAGAAAGCGAGCTTAGTGATACTTGTGGGCCAGGGCATTAGCCACACCAGCCACCACTTTCTGATAGGCAGCCTGCACTGGTGGGGTGAATTCTTTGCCAAAGTGATGGGCCAGCACACAGACCAGCACGTTGCCCAGGAG"
}}
assert results['NM_000518.4:c.316_*100del']['primary_assembly_loci']['hg38'] == {
"hgvs_genomic_description": "NC_000011.10:g.5225498_5225726del",
"vcf": {
"alt": "A",
"chr": "chr11",
"pos": "5225497",
"ref": "AATCCAGATGCTCAAGGCCCTTCATAATATCCCCCAGTTTAGTAGTTGGACTTAGGGAACAAAGGAACCTTTAATAGAAATTGGACAGCAAGAAAGCGAGCTTAGTGATACTTGTGGGCCAGGGCATTAGCCACACCAGCCACCACTTTCTGATAGGCAGCCTGCACTGGTGGGGTGAATTCTTTGCCAAAGTGATGGGCCAGCACACAGACCAGCACGTTGCCCAGGAG"
}}
assert results['NM_000518.4:c.316_*100del']['primary_assembly_loci']['grch37'] == {
"hgvs_genomic_description": "NC_000011.9:g.5246728_5246956del",
"vcf": {
"alt": "A",
"chr": "11",
"pos": "5246727",
"ref": "AATCCAGATGCTCAAGGCCCTTCATAATATCCCCCAGTTTAGTAGTTGGACTTAGGGAACAAAGGAACCTTTAATAGAAATTGGACAGCAAGAAAGCGAGCTTAGTGATACTTGTGGGCCAGGGCATTAGCCACACCAGCCACCACTTTCTGATAGGCAGCCTGCACTGGTGGGGTGAATTCTTTGCCAAAGTGATGGGCCAGCACACAGACCAGCACGTTGCCCAGGAG"
}}
assert results['NM_000518.4:c.316_*100del']['primary_assembly_loci']['grch38'] == {
'hgvs_genomic_description': 'NC_000011.10:g.5225498_5225726del', 'vcf': {'alt': 'DEL', 'chr': '11', 'pos': '5225498-5225726', 'ref': 'N'}}
"hgvs_genomic_description": "NC_000011.10:g.5225498_5225726del",
"vcf": {
"alt": "A",
"chr": "11",
"pos": "5225497",
"ref": "AATCCAGATGCTCAAGGCCCTTCATAATATCCCCCAGTTTAGTAGTTGGACTTAGGGAACAAAGGAACCTTTAATAGAAATTGGACAGCAAGAAAGCGAGCTTAGTGATACTTGTGGGCCAGGGCATTAGCCACACCAGCCACCACTTTCTGATAGGCAGCCTGCACTGGTGGGGTGAATTCTTTGCCAAAGTGATGGGCCAGCACACAGACCAGCACGTTGCCCAGGAG"
}}
assert results['NM_000518.4:c.316_*100del']['reference_sequence_records'] == {
'transcript': 'https://www.ncbi.nlm.nih.gov/nuccore/NM_000518.4',
'protein': 'https://www.ncbi.nlm.nih.gov/nuccore/NP_000509.1',
Expand Down Expand Up @@ -30368,7 +30392,7 @@ def test_variant333(self):

def test_variant334(self):
variant = 'NM_000061.2:c.588_589insCTACATAG'
results = self.vv.validate(variant, 'GRCh37', 'all').format_as_dict(test=True)
results = self.vv.validate(variant, 'GRCh37', 'all', shorthand_vcf=True).format_as_dict(test=True)
print(results)

assert results['flag'] == 'gene_variant'
Expand Down Expand Up @@ -31304,7 +31328,7 @@ def test_issue_733a(self):
results = self.vv.validate('chr11:118650341:C:T', 'GRCh37','NM_004397.6').format_as_dict(test=True)
assert 'NM_004397.6:c.369G>A' in results

def polyadenylation_a(self):
def test_polyadenylation_a(self):
# Test that it fails for genome mismatch
results = self.vv.validate('NM_001424184.1:c.438G>A', 'GRCh38', 'all').format_as_dict(test=True)
assert 'NM_001424184.1:c.438G>A' in results
Expand Down Expand Up @@ -31353,17 +31377,30 @@ def test_issue_801(self):
assert "NM_014249.4:c.*557del" in results.keys()
assert "NM_001281446.1:c.*557del" in results.keys()

def issue_818(self):
def test_issue_818(self):
results = self.vv.validate('NC_000022.10:g.19929250_19929251insCCCCGCC', 'GRCh38', 'mane_select', liftover_level=True).format_as_dict(test=True)
assert "NM_006440.5:c.70_76dup" in results.keys()
assert results["NM_006440.5:c.70_76dup"][
"hgvs_predicted_protein_consequence"] == {
"lrg_slr": "LRG_417p1:p.V26Gfs*132",
"lrg_tlr": "LRG_417p1:p.Val26GlyfsTer132",
"slr": "NP_006431.2:p.V26Gfs*132",
"tlr": "NP_006431.2:p.Val26GlyfsTer132"
"slr": "NP_006431.2:p.(V26Gfs*132)",
"tlr": "NP_006431.2:p.(Val26GlyfsTer132)"
}

def test_alt_gapping_bug(self):
results = self.vv.validate('chr16:2089739:G:C', 'GRCh38', 'mane_select', liftover_level=True).format_as_dict(test=True)
assert "NM_001009944.3:c.12900C>G" in results.keys()
assert "NT_187607.1:g.705079dup" not in str(results["NM_001009944.3:c.12900C>G"]["alt_genomic_loci"])

def test_alt_gapping_bug_b(self):
results = self.vv.validate('chr16:2089760:C:G', 'GRCh38', 'mane_select', liftover_level=True).format_as_dict(test=True)
assert "NM_001009944.3:c.12879G>C" in results.keys()
assert "NT_187607.1:g.705077_705079dup" not in str(results["NM_001009944.3:c.12879G>C"]["alt_genomic_loci"])

def test_alt_gapping_bug_c(self):
results = self.vv.validate('chr16:2090285:C:CT', 'GRCh38', 'mane_select', liftover_level=True).format_as_dict(test=True)
assert "NM_001009944.3:c.12443dup" in results.keys()
assert "NT_187607.1:g.713119_713120insTT" not in str(results["NM_001009944.3:c.12443dup"]["alt_genomic_loci"])

# <LICENSE>
# Copyright (C) 2016-2026 VariantValidator Contributors
#
Expand Down