diff --git a/VariantValidator/modules/complex_descriptions.py b/VariantValidator/modules/complex_descriptions.py
index 991618f7..89351f0e 100644
--- a/VariantValidator/modules/complex_descriptions.py
+++ b/VariantValidator/modules/complex_descriptions.py
@@ -2,8 +2,10 @@
import copy
from vvhgvs.assemblymapper import AssemblyMapper
from VariantValidator.modules import utils as vv_utils, format_converters
-from VariantValidator.modules.hgvs_utils import hgvs_obj_from_existing_edit
+from VariantValidator.modules.hgvs_utils import hgvs_obj_from_existing_edit,\
+ _hgvs_offset_pos_from_str_in
import vvhgvs.exceptions
+from vvhgvs.enums import Datum, ValidationLevel
from vvhgvs.location import Interval
class UncertainConversionError(Exception):
@@ -12,8 +14,10 @@ class UncertainConversionError(Exception):
# fuzzy but specified ended intervals, take a normal interval for both ends
# can take a pair of Intervals or BaseOffsetIntervals
class FEInterval(Interval):
- uncertain = True
- pass
+ def __init__(self,start = None,end = None):
+ self.uncertain = True
+ self.start = start
+ self.end = end
def validate(self):
if self.start:
(res, msg) = self.start.validate()
@@ -258,12 +262,12 @@ def fuzzy_ends(my_variant, validator):
raise FuzzyRangeError("Fuzzy/unknown variant end position in submitted variant description")
else:
- if "?" in str(my_variant.hgvs_formatted.posedit.pos):
- if "?" in str(my_variant.hgvs_formatted.posedit.pos.end) and "?" not in str(
- my_variant.hgvs_formatted.posedit.pos.start):
+ if "?" in str(my_variant.quibble.posedit.pos):
+ if "?" in str(my_variant.quibble.posedit.pos.end) and "?" not in str(
+ my_variant.quibble.posedit.pos.start):
raise FuzzyPositionError("Fuzzy/unknown variant end position in submitted variant description")
- elif "?" in str(my_variant.hgvs_formatted.posedit.pos.start) and "?" not in str(
- my_variant.hgvs_formatted.posedit.pos.end):
+ elif "?" in str(my_variant.quibble.posedit.pos.start) and "?" not in str(
+ my_variant.quibble.posedit.pos.end):
raise FuzzyPositionError("Fuzzy/unknown variant start position in submitted variant description")
else:
raise FuzzyPositionError("Fuzzy/unknown variant start and end positions "
@@ -369,9 +373,28 @@ def uncertain_positions(my_variant, validator):
# Genomic Variants
if "NC_" in my_variant.quibble:
- my_variant.hgvs_genomic = my_variant.quibble
- start_pos = position_1.split("_")[0]
- end_pos = position_2.split("_")[1]
+ start_pos, _sep, start_end = position_1.partition("_")
+ o_start_pos, start_end = _hgvs_offset_pos_from_str_in(
+ start_pos,
+ None,
+ ref_type=var_type,
+ end=start_end)
+ start = Interval(start=o_start_pos,end=start_end)
+ end_start, _sep, end_pos = position_2.partition("_")
+ end_start, o_end_pos = _hgvs_offset_pos_from_str_in(
+ end_start,
+ None,
+ ref_type=var_type,
+ end=end_pos)
+ end = Interval(start=end_start,end=o_end_pos)
+ full_obj = hgvs_obj_from_existing_edit(
+ accession,
+ var_type,
+ FEInterval(
+ start=start,
+ end=end),
+ edit)
+ my_variant.hgvs_genomic = full_obj
parsed_v3 = hgvs_obj_from_existing_edit(
accession,
var_type,
@@ -438,7 +461,6 @@ def uncertain_positions(my_variant, validator):
end=t_position_2),
edit)
- my_variant.hgvs_coding = tx_variant
my_variant.quibble = copy.copy(tx_variant)
my_variant.hgvs_transcript_variant = tx_variant
else:
@@ -472,15 +494,15 @@ def uncertain_positions(my_variant, validator):
end=g_position_2),
edit)
my_variant.hgvs_genomic = gen_variant
- my_variant.hgvs_coding = hgvs_obj_from_existing_edit(
+ hgvs_coding = hgvs_obj_from_existing_edit(
parsed_v1.ac,
parsed_v2.type,
FEInterval(start=parsed_v1.posedit.pos,
end=parsed_v2.posedit.pos),
edit
)
- my_variant.hgvs_transcript_variant = copy.copy(my_variant.hgvs_coding)
- my_variant.quibble = copy.copy(my_variant.hgvs_coding)
+ my_variant.hgvs_transcript_variant = copy.copy(hgvs_coding)
+ my_variant.quibble = copy.copy(hgvs_coding)
my_variant.output_type_flag = "gene"
elif ")_(" not in my_variant.quibble and not "?" in my_variant.quibble:
@@ -489,7 +511,6 @@ def uncertain_positions(my_variant, validator):
accession, _sep, var_type_and_posedit = my_variant.quibble.partition(':')
var_type, _sep ,position_and_edit = var_type_and_posedit.partition(".(")
position_1, variation = position_and_edit.split(")")
- v1 = f"{accession}:{var_type}.{position_1}="
my_variant.reftype = f":{var_type}."
try:
if var_type == 'p':
@@ -517,7 +538,7 @@ def uncertain_positions(my_variant, validator):
raise IncompatibleTypeError(str(e))
elif "base start position must be <= end position" in str(e):
raise InvalidRangeError(f"{str(e)} in position {str(parsed_v1.posedit.pos)}")
- elif re.search("[+-]", str(parsed_v1.posedit.pos)) or re.search("[+-]", str(parsed_v1.posedit.pos)):
+ elif "+" in str(parsed_v1.posedit.pos) or "-" in str(parsed_v1.posedit.pos):
pass
else:
raise InvalidRangeError(f"{position_1} is an invalid range for "
@@ -529,8 +550,23 @@ def uncertain_positions(my_variant, validator):
# Genomic Variants
if "NC_" in my_variant.quibble:
- my_variant.hgvs_genomic = my_variant.quibble
-
+ start_pos, _sep, start_end = position_1.partition("_")
+ start_pos, end_pos = _hgvs_offset_pos_from_str_in(
+ start_pos,
+ None,
+ end=start_end,
+ ref_type=var_type)
+ pos = Interval(start=start_pos,end=end_pos)
+ try:
+ full_obj = hgvs_obj_from_existing_edit(
+ accession,
+ var_type,
+ pos,
+ edit)
+ except Exception as e:
+ raise e
+ full_obj.posedit.pos.uncertain = True
+ my_variant.hgvs_genomic = full_obj
# Make select_transcriopts "select" unless specified
if (validator.select_transcripts != "select" and ("NM_" in validator.select_transcripts or
"ENST" in validator.select_transcripts) and
@@ -567,8 +603,6 @@ def uncertain_positions(my_variant, validator):
ptv1[0].posedit.pos,
edit)
tx_variant.posedit.pos.uncertain = True
-
- my_variant.hgvs_coding = tx_variant
my_variant.quibble = copy.copy(tx_variant)
my_variant.hgvs_transcript_variant = tx_variant
else:
@@ -587,14 +621,14 @@ def uncertain_positions(my_variant, validator):
gen_variant.posedit.pos.uncertain = True
my_variant.hgvs_genomic = gen_variant
- my_variant.hgvs_coding = hgvs_obj_from_existing_edit(
+ hgvs_coding = hgvs_obj_from_existing_edit(
parsed_v1.ac,
parsed_v1.type,
parsed_v1.posedit.pos,
edit)
- my_variant.hgvs_coding.posedit.pos.uncertain = True
- my_variant.hgvs_transcript_variant = my_variant.hgvs_coding
- my_variant.quibble = copy.copy(my_variant.hgvs_coding)
+ hgvs_coding.posedit.pos.uncertain = True
+ my_variant.hgvs_transcript_variant = hgvs_coding
+ my_variant.quibble = copy.copy(hgvs_coding)
my_variant.output_type_flag = "gene"
else:
diff --git a/VariantValidator/modules/exon_numbering.py b/VariantValidator/modules/exon_numbering.py
index ac455c76..15ce9205 100644
--- a/VariantValidator/modules/exon_numbering.py
+++ b/VariantValidator/modules/exon_numbering.py
@@ -37,7 +37,7 @@ def finds_exon_number(variant, validator):
info_dict = {}
for i in range(len(response_dictionary["transcripts"])):
- if response_dictionary["transcripts"][i]["reference"] == variant.hgvs_coding.ac:
+ if response_dictionary["transcripts"][i]["reference"] == variant.quibble.ac:
# Create record
info_dict[(response_dictionary["transcripts"][i]["reference"])] = {}
@@ -53,7 +53,7 @@ def finds_exon_number(variant, validator):
# Step 2 - Get the necessary variant information
# Find the variant position from the variant nomenclature
- coordinates = str(variant.hgvs_coding.posedit.pos)
+ coordinates = str(variant.quibble.posedit.pos)
# Identify start and end of variant from input coordinates
if '_' in coordinates:
@@ -72,9 +72,9 @@ def finds_exon_number(variant, validator):
# Create c_to_n varint
try:
- to_n = validator.vm.c_to_n(variant.hgvs_coding)
+ to_n = validator.vm.c_to_n(variant.quibble)
except vvhgvs.exceptions.HGVSInvalidVariantError:
- to_n = variant.hgvs_coding
+ to_n = variant.quibble
"""
This for loop identifies the exon/intron number for the transcript
@@ -85,8 +85,8 @@ def finds_exon_number(variant, validator):
keys: start_exon and end_exon
values: start and position of variant in the reference sequence
"""
- exon_structure_dict = info_dict[variant.hgvs_coding.ac]["exon_structure_dict"]
- coding_start = info_dict[variant.hgvs_coding.ac]['coding_start']
+ exon_structure_dict = info_dict[variant.quibble.ac]["exon_structure_dict"]
+ coding_start = info_dict[variant.quibble.ac]['coding_start']
if coding_start is None:
coding_start = 1
diff --git a/VariantValidator/modules/format_converters.py b/VariantValidator/modules/format_converters.py
index 42d352c7..3d3a4ba5 100644
--- a/VariantValidator/modules/format_converters.py
+++ b/VariantValidator/modules/format_converters.py
@@ -106,7 +106,9 @@ def initial_format_conversions(variant, validator, select_transcripts_dict_plus_
# fail if un-corrected errors persist (warning should already have been generated)
if toskip:
return True
-
+ # make sure ref type and source are set if we intend to continue
+ variant.set_reftype()
+ variant.set_refsource()
# Tackle compound variant descriptions NG or NC (NM_) i.e. correctly input NG/NC_(NM_):c.
intronic_converter(variant, validator)
return False
@@ -1077,21 +1079,19 @@ def lrg_to_refseq(variant, validator):
"""
caution = ''
if variant.refsource == 'LRG':
- if variant.hgvs_formatted.ac.startswith('LRG') and variant.hgvs_formatted.ac[3:4].isdigit():
- reference = variant.hgvs_formatted.ac.replace('LRG', 'LRG_')
- caution = variant.hgvs_formatted.ac + ' updated to ' + reference + ': '
- variant.hgvs_formatted.ac = reference
- variant.set_quibble(variant.hgvs_formatted)
+ if variant.quibble.ac.startswith('LRG') and variant.quibble.ac[3:4].isdigit():
+ reference = variant.quibble.ac.replace('LRG', 'LRG_')
+ caution = variant.quibble.ac + ' updated to ' + reference + ': '
+ variant.quibble.ac = reference
if re.match(r'^LRG_\d+t\d+$', variant.quibble.ac):
lrg_reference = variant.quibble.ac
refseqtrans_reference = validator.db.get_refseq_transcript_id_from_lrg_transcript_id(lrg_reference)
if refseqtrans_reference != 'none':
- old_var_str = str(variant.hgvs_formatted)
- variant.hgvs_formatted.ac = refseqtrans_reference
- variant.set_quibble(variant.hgvs_formatted)
+ old_var_str = str(variant.quibble)
+ variant.quibble.ac = refseqtrans_reference
caution += old_var_str + ' automapped to equivalent RefSeq record ' \
- '' + str(variant.hgvs_formatted)
+ '' + str(variant.quibble)
variant.warnings.append(caution)
logger.info(caution)
@@ -1099,11 +1099,10 @@ def lrg_to_refseq(variant, validator):
lrg_reference = variant.quibble.ac
refseqprot_reference = validator.db.get_refseq_protein_id_from_lrg_protein_id(lrg_reference)
if refseqprot_reference != 'none':
- old_var_str = str(variant.hgvs_formatted)
- variant.hgvs_formatted.ac = refseqprot_reference
- variant.set_quibble(variant.hgvs_formatted)
+ old_var_str = str(variant.quibble)
+ variant.quibble.ac = refseqprot_reference
caution += old_var_str + ' automapped to equivalent RefSeq record ' \
- '' + str(variant.hgvs_formatted)
+ '' + str(variant.quibble)
variant.warnings.append(caution)
logger.info(caution)
@@ -1111,11 +1110,10 @@ def lrg_to_refseq(variant, validator):
lrg_reference = variant.quibble.ac
refseqgene_reference = validator.db.get_refseq_id_from_lrg_id(lrg_reference)
if refseqgene_reference != 'none':
- old_var_str = str(variant.hgvs_formatted)
- variant.hgvs_formatted.ac = refseqgene_reference
- variant.set_quibble(variant.hgvs_formatted)
+ old_var_str = str(variant.quibble)
+ variant.quibble.ac = refseqgene_reference
caution += old_var_str + ' automapped to equivalent RefSeq record ' \
- '' + str(variant.hgvs_formatted)
+ '' + str(variant.quibble)
variant.warnings.append(caution)
logger.info(caution)
@@ -1124,14 +1122,14 @@ def mitochondrial(variant, validator):
"""Will check if variant is mitochondrial and if so it will reformat the type to 'm' and save a value to the variant
hgvs_genomic attribute"""
- if variant.reftype == ':m.' or variant.hgvs_formatted.ac == 'NC_012920.1' or \
- variant.hgvs_formatted.ac == 'NC_001807.4':
+ if variant.reftype == ':m.' or variant.quibble.ac == 'NC_012920.1' or \
+ variant.quibble.ac == 'NC_001807.4':
# set flag
variant.output_type_flag = 'mitochondrial'
# Ensure the correct reference sequence type is used, if not, warn the user
- hgvs_mito = copy.deepcopy(variant.hgvs_formatted)
+ hgvs_mito = copy.deepcopy(variant.quibble)
if hgvs_mito.type == 'g' and (hgvs_mito.ac == 'NC_012920.1' or hgvs_mito.ac == 'NC_001807.4'):
hgvs_mito.type = 'm'
if "NC_012920.1" in hgvs_mito.ac and "hg19" in variant.selected_assembly:
@@ -1164,7 +1162,7 @@ def mitochondrial(variant, validator):
# Check for movement during normalization
try:
- norm_check = variant.hn.normalize(variant.hgvs_formatted)
+ norm_check = variant.hn.normalize(variant.quibble)
if hgvs_mito.posedit.pos != norm_check.posedit.pos:
norm_check.type = "m"
error = "%s updated to %s" % (fn.valstr(hgvs_mito), fn.valstr(norm_check))
@@ -1184,7 +1182,6 @@ def mitochondrial(variant, validator):
# Add a description of the reference sequence type and continue
variant.hgvs_genomic = hgvs_mito
if len(rel_var) == 0:
- variant.genomic_g = unset_hgvs_obj_ref(hgvs_mito)
variant.description = 'Homo sapiens mitochondrion, complete genome'
logger.info('Homo sapiens mitochondrion, complete genome')
return True
@@ -1198,7 +1195,7 @@ def proteins(variant, validator):
error = None
hgvs_object = None
# Try to validate the variant
- hgvs_object = variant.hgvs_formatted
+ hgvs_object = variant.quibble
try:
validator.vr.validate(hgvs_object)
@@ -1375,17 +1372,17 @@ def rna(variant, validator):
convert r, into c.
"""
if variant.reftype == ':r.' or ":r." in variant.original:
- if ":r.(" in str(variant.hgvs_formatted):
- if type(variant.hgvs_formatted) is str:
- strip_prediction = str(variant.hgvs_formatted).replace("(", "")
+ if ":r.(" in str(variant.quibble):
+ if type(variant.quibble) is str:
+ strip_prediction = str(variant.quibble).replace("(", "")
strip_prediction = strip_prediction[:-1]
hgvs_input = validator.hp.parse_hgvs_variant(strip_prediction)
else:
- hgvs_input = variant.hgvs_formatted
+ hgvs_input = variant.quibble
hgvs_input.posedit.pos.uncertain = False
#hgvs_input.posedit.uncertain = False
else:
- hgvs_input = variant.hgvs_formatted
+ hgvs_input = variant.quibble
tx_info = validator.hdp.get_tx_identity_info(hgvs_input.ac)
if tx_info[3] is None:
@@ -1403,7 +1400,7 @@ def rna(variant, validator):
variant.warnings.append(error)
logger.info(str(error))
return True
- variant.hgvs_formatted = hgvs_c
+ variant.quibble = hgvs_c
# Create variant.rna_data dictionary
rnd = VariantValidator.modules.rna_formatter.RnaDescriptions(validator.alt_aln_method,
@@ -1492,4 +1489,4 @@ def uncertain_pos(variant, validator):
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-#
\ No newline at end of file
+#
diff --git a/VariantValidator/modules/gapped_mapping.py b/VariantValidator/modules/gapped_mapping.py
index 9a9b0572..bdd76690 100644
--- a/VariantValidator/modules/gapped_mapping.py
+++ b/VariantValidator/modules/gapped_mapping.py
@@ -409,11 +409,11 @@ def gapped_g_to_c(self, rel_var, select_transcripts_dict):
# take a look at the input genomic variant for potential base salvage
stash_ac = vcf_dict['chr']
- stash_input = self.variant.post_format_conversion
+ stash_input = self.variant.quibble
if type(stash_input) is str:
stash_input = self.validator.hp.parse_hgvs_variant(stash_input)
# Re-Analyse genomic positions
- if 'NG_' in str(self.variant.hgvs_formatted):
+ if 'NG_' in str(self.variant.quibble):
c = rel_var[0]
if hasattr(c.posedit.edit, 'ref') and c.posedit.edit.ref is not None:
c.posedit.edit.ref = c.posedit.edit.ref.upper()
diff --git a/VariantValidator/modules/gene2transcripts.py b/VariantValidator/modules/gene2transcripts.py
index 62f93bb7..ff5257c4 100644
--- a/VariantValidator/modules/gene2transcripts.py
+++ b/VariantValidator/modules/gene2transcripts.py
@@ -124,7 +124,7 @@ def gene2transcripts(g2t,
# Gather transcript information lists
if bypass_web_searches is True:
tx_for_gene = []
- tx_info = g2t.hdp.get_tx_identity_info(query.hgvs_coding.ac)
+ tx_info = g2t.hdp.get_tx_identity_info(query.quibble.ac)
# Add primary assembly queries
for builds in query.primary_assembly_loci.keys():
@@ -132,7 +132,7 @@ def gene2transcripts(g2t,
tx_for_gene.append([query.gene_symbol,
tx_info[3],
0,
- query.hgvs_coding.ac,
+ query.quibble.ac,
query.primary_assembly_loci[builds]['hgvs_genomic_description'].ac,
validator.alt_aln_method])
@@ -141,7 +141,7 @@ def gene2transcripts(g2t,
tx_for_gene.append([query.gene_symbol,
tx_info[3],
0,
- query.hgvs_coding.ac,
+ query.quibble.ac,
query.hgvs_refseqgene_variant.ac,
validator.alt_aln_method])
diff --git a/VariantValidator/modules/hgvs_utils.py b/VariantValidator/modules/hgvs_utils.py
index 2f2a8801..4ea6ffe2 100644
--- a/VariantValidator/modules/hgvs_utils.py
+++ b/VariantValidator/modules/hgvs_utils.py
@@ -164,10 +164,16 @@ def unset_hgvs_obj_ref(hgvs):
"""
Remove/unset ref bases from hgvs object, in the manner needed for output,
but without re-parsing from text.
+
+ Skips known cases where ref should not be unset, including any unc
+ positions, or other N ref.
"""
+ if hgvs.posedit.pos.uncertain:
+ return hgvs
edit = hgvs.posedit.edit
if edit.type in ['inv', 'dup']:
- edit.ref = ''
+ if edit.ref and not 'N' in edit.ref:
+ edit.ref = ''
elif edit.ref is not None and edit.alt is not None:
#if #edit.alt != edit.ref and \
if len(edit.alt) == 1 and len(edit.ref) == 1:
@@ -2695,16 +2701,16 @@ def hgvs_ref_alt(hgvs_variant, sf):
return ref_alt_dict
-def incomplete_alignment_mapping_t_to_g(validator, variant):
+def incomplete_alignment_mapping_t_to_g(validator, variant,t_hgvs_var):
output = None
- mapping_options = variant.map_dat.mapping_options(variant.input_parses.ac,hdp=validator.hdp)
+ mapping_options = validator.hdp.get_tx_mapping_options(t_hgvs_var.ac,hdp=validator.hdp)
for option in mapping_options:
if option[2] == validator.alt_aln_method and "NC_" not in option[1]:
in_assembly = seq_data.to_chr_num_refseq(option[1], variant.primary_assembly)
if in_assembly is not None:
try:
- output = validator.vm.t_to_g(variant.input_parses, option[1])
- if variant.input_parses.posedit.edit.type == "identity":
+ output = validator.vm.t_to_g(t_hgvs_var, option[1])
+ if t_hgvs_var.posedit.edit.type == "identity":
output.posedit.edit.alt = output.posedit.edit.ref
except vvhgvs.exceptions.HGVSError:
pass
diff --git a/VariantValidator/modules/mappers.py b/VariantValidator/modules/mappers.py
index 44278613..6837c045 100644
--- a/VariantValidator/modules/mappers.py
+++ b/VariantValidator/modules/mappers.py
@@ -22,8 +22,8 @@ class TranscriptMappingError(Exception):
pass
def gene_to_transcripts(variant, validator, select_transcripts_dict, batch_list):
- logger.info(f"Mapping {variant.hgvs_formatted} to transcripts")
- g_query = variant.hgvs_formatted
+ logger.info(f"Mapping {variant.quibble} to transcripts")
+ g_query = variant.quibble
# set hdp for exon mapping fetch before first use
if not variant.map_dat.hdp:
variant.map_dat.hdp = validator.hdp
@@ -58,7 +58,7 @@ def gene_to_transcripts(variant, validator, select_transcripts_dict, batch_list)
# use updated position if normalized to a different position
if g_query.posedit.pos != g_test.posedit.pos:
variant.hgvs_genomic = g_test
- variant.hgvs_formatted = g_test
+ variant.quibble = g_test
else:
variant.hgvs_genomic = g_query
@@ -135,14 +135,14 @@ def gene_to_transcripts(variant, validator, select_transcripts_dict, batch_list)
except Exception as e:
logger.info(f"nw_rel_var creation failed with exception {str(e)}")
raise TranscriptMappingError(f"Encountered an issue when mapping genomic description "
- f"{variant.hgvs_formatted} to available transcripts")
+ f"{variant.quibble} to available transcripts")
rel_var = nw_rel_var
if len(rel_var) == 0:
# Check for NG_
- if variant.hgvs_formatted.ac.startswith('NG_'):
- hgvs_refseqgene =variant.hgvs_formatted
+ if variant.quibble.ac.startswith('NG_'):
+ hgvs_refseqgene =variant.quibble
# Convert to chromosomal position
refseqgene_data = validator.rsg_to_chr(hgvs_refseqgene, variant.primary_assembly, variant.hn)
# There should only ever be one description returned
@@ -153,11 +153,11 @@ def gene_to_transcripts(variant, validator, select_transcripts_dict, batch_list)
variant.output_type_flag = 'intergenic'
# set genomic and where available RefSeqGene outputs
variant.warnings.append(no_tx_found_error)
- error = 'TranscriptIdentificationWarning: Mapping unavailable for RefSeqGene ' + str(variant.hgvs_formatted) + \
+ error = 'TranscriptIdentificationWarning: Mapping unavailable for RefSeqGene ' + str(variant.quibble) + \
' using alignment method = ' + validator.alt_aln_method
variant.warnings.append(error)
- variant.genomic_r = variant.hgvs_formatted
- variant.refseqgene_variant = variant.hgvs_formatted
+ variant.hgvs_refseqgene_variant = variant.quibble
+ variant.refseqgene_variant = variant.quibble
return True
# Extract data
@@ -165,15 +165,16 @@ def gene_to_transcripts(variant, validator, select_transcripts_dict, batch_list)
genomic_input = refseqgene_data['hgvs_genomic']
# re_submit
# Tag the line so that it is not written out
- variant.warnings.append(str(variant.hgvs_formatted) + ' automapped to genome position ' +
+ variant.warnings.append(str(variant.quibble) + ' automapped to genome position ' +
str(genomic_input))
+ variant.write = False
query = Variant(variant.original, quibble=genomic_input, warnings=variant.warnings,
primary_assembly=variant.primary_assembly, order=variant.order,
selected_assembly=variant.selected_assembly)
batch_list.append(query)
logger.info('Submitting new variant with format %s', genomic_input)
else:
- error = 'TranscriptIdentificationWarning: Mapping unavailable for RefSeqGene ' + str(variant.hgvs_formatted) + \
+ error = 'TranscriptIdentificationWarning: Mapping unavailable for RefSeqGene ' + str(variant.quibble) + \
' using alignment method = ' + validator.alt_aln_method
variant.warnings.append(error)
logger.info(str(error))
@@ -217,9 +218,8 @@ def gene_to_transcripts(variant, validator, select_transcripts_dict, batch_list)
variant.output_type_flag = 'intergenic'
# set genomic and where available RefSeqGene outputs
variant.warnings.append(error)
- variant.genomic_g = unset_hgvs_obj_ref(variant.hgvs_genomic)
- variant.genomic_r = rsg_data[0]
- logger.info(str(error))
+ variant.hgvs_refseqgene_variant = rsg_data[0]
+ logger.warning(str(error))
return True
else:
error = 'Validation will fail if the selected chromosome reference sequence does not corresponds to ' \
@@ -267,20 +267,15 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
valid = False
caution = ''
error = ''
- # Collect information for genomic level validation
- obj = variant.hgvs_formatted
- if type(obj) is str: #still happens for some error cases
- variant.hgvs_formatted = validator.hp.parse_hgvs_variant(
- variant.hgvs_formatted)
- obj = variant.hgvs_formatted
- tx_ac = obj.ac
-
- quibble_input = str(variant.quibble)
- quibble_input_hgvs_obj = variant.quibble
- if isinstance(quibble_input_hgvs_obj, str):
- quibble_input_hgvs_obj = validator.hp.parse_hgvs_variant(variant.quibble)
- formatted_variant = str(variant.hgvs_formatted)
- out_hgvs_obj = variant.hgvs_formatted
+ if isinstance(variant.quibble, str):
+ variant.quibble = validator.hp.parse_hgvs_variant(variant.quibble)
+ # preserved input variables
+ input_formatted_variant= str(variant.quibble)
+ input_hgvs_obj = copy.copy(variant.quibble)
+ tx_ac = input_hgvs_obj.ac
+ # to be changed versions for output
+ formatted_variant = str(variant.quibble)
+ out_hgvs_obj = variant.quibble
# Do we keep it?
if (validator.select_transcripts != 'all' and validator.select_transcripts != 'raw') \
and "select" not in validator.select_transcripts and \
@@ -288,7 +283,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
if tx_ac not in list(select_transcripts_dict_plus_version.keys()):
# By marking it as Do Not Write and continuing through the validation loop
if ":g." not in variant.original:
- error = (f"TranscriptSelectionError: Variant {variant.hgvs_formatted} is not in the list of "
+ error = (f"TranscriptSelectionError: Variant {variant.quibble} is not in the list of "
f"transcripts selected for validation {validator.select_transcripts}")
logger.info(error)
variant.warnings.append(error)
@@ -303,10 +298,10 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
# First task is to get the genomic equivalent, and output a useful error messages if it can't be found.
try:
reset_g_origin = False
- if obj.rel_ac.startswith('NG_'):
+ if out_hgvs_obj.rel_ac.startswith('NG_'):
reset_g_origin=True
to_g = validator.myevm_t_to_g(
- obj,
+ out_hgvs_obj,
variant.no_norm_evm,
variant.primary_assembly,
variant.hn,
@@ -352,7 +347,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
plus = re.compile(r"\d\+\d") # finds digit + digit
minus = re.compile(r"\d-\d") # finds digit - digit
- if plus.search(quibble_input) or minus.search(quibble_input):
+ if plus.search(input_formatted_variant) or minus.search(input_formatted_variant):
if 'error' in str(to_g):
if validator.alt_aln_method != 'genebuild':
error = "If the following error message does not address the issue and the problem persists please " \
@@ -371,7 +366,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
else:
# Incorrect genomic reference base
try:
- check_base = validator.nr_vm.t_to_g(obj, to_g.ac)
+ check_base = validator.nr_vm.t_to_g(out_hgvs_obj, to_g.ac)
except vvhgvs.exceptions.HGVSError:
pass
else:
@@ -381,26 +376,28 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
if "does not agree with reference sequence" in str(e):
a, b = re.findall(r'\(([GATC]+)\)', str(e))[:2]
if (len(a) == len(b) and
- obj.posedit.edit.type == check_base.posedit.edit.type):
- error = f"{str(obj)} mapped to {str(e)}"
+ out_hgvs_obj.posedit.edit.type == check_base.posedit.edit.type):
+ error = f"{str(out_hgvs_obj)} mapped to {str(e)}"
variant.warnings.append(error)
logger.info(error)
# Insertions at exon boundaries are miss-handled by vm.g_to_t
- if (obj.posedit.edit.type == 'ins' and
- obj.posedit.pos.start.offset == 0 and
- obj.posedit.pos.end.offset != 0) or (obj.posedit.edit.type == 'ins' and
- obj.posedit.pos.start.offset != 0 and
- obj.posedit.pos.end.offset == 0):
- formatted_variant = str(obj)
- out_hgvs_obj = obj
+ if (
+ out_hgvs_obj.posedit.edit.type == 'ins' and
+ out_hgvs_obj.posedit.pos.start.offset == 0 and
+ out_hgvs_obj.posedit.pos.end.offset != 0
+ ) or (
+ out_hgvs_obj.posedit.edit.type == 'ins' and
+ out_hgvs_obj.posedit.pos.start.offset != 0 and
+ out_hgvs_obj.posedit.pos.end.offset == 0):
+ formatted_variant = str(out_hgvs_obj)
else:
try:
out_hgvs_obj = validator.myevm_g_to_t(variant.evm, to_g, tx_ac)
formatted_variant = str(out_hgvs_obj)
except vvhgvs.exceptions.HGVSError as e:
if "Alignment is incomplete" in str(e):
- to_g = hgvs_utils.incomplete_alignment_mapping_t_to_g(validator, variant)
+ to_g = hgvs_utils.incomplete_alignment_mapping_t_to_g(validator, variant,variant.quibble)
if to_g is None:
error = str(e)
variant.warnings.append(error)
@@ -411,7 +408,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
out_hgvs_obj = validator.vm.g_to_t(to_g, tx_ac)
formatted_variant = str(out_hgvs_obj)
- elif ':g.' in quibble_input:
+ elif ':g.' in formatted_variant:
if plus.search(formatted_variant) or minus.search(formatted_variant):
to_g = validator.genomic(out_hgvs_obj, variant.no_norm_evm, variant.primary_assembly, variant)
if 'error' in str(to_g):
@@ -430,13 +427,14 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
return True
else:
# Insertions at exon boundaries are miss-handled by vm.g_to_t
- if (obj.posedit.edit.type == 'ins' and
- obj.posedit.pos.start.offset == 0 and
- obj.posedit.pos.end.offset != 0) or (obj.posedit.edit.type == 'ins' and
- obj.posedit.pos.start.offset != 0 and
- obj.posedit.pos.end.offset == 0):
- out_hgvs_obj = obj
- formatted_variant = str(obj)
+ if (
+ out_hgvs_obj.posedit.edit.type == 'ins' and
+ out_hgvs_obj.posedit.pos.start.offset == 0 and
+ out_hgvs_obj.posedit.pos.end.offset != 0) or (
+ out_hgvs_obj.posedit.edit.type == 'ins' and
+ out_hgvs_obj.posedit.pos.start.offset != 0 and
+ out_hgvs_obj.posedit.pos.end.offset == 0):
+ formatted_variant = str(out_hgvs_obj)
else:
# Normalize was I believe to replace ref. Mapping does this anyway
# to_g = hn.normalize(to_g)
@@ -446,7 +444,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
else:
# Normalize the variant
try:
- h_variant = variant.hn.normalize(obj)
+ h_variant = variant.hn.normalize(out_hgvs_obj)
except vvhgvs.exceptions.HGVSUnsupportedOperationError as error:
if 'Unsupported normalization of variants spanning the exon-intron boundary' in str(error):
formatted_variant = formatted_variant
@@ -471,17 +469,17 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
# Tackle the plus intronic offset
cck = False
- if plus.search(quibble_input):
+ if plus.search(input_formatted_variant):
# Regular expression catches the start of the interval only based on .00+00 pattern
inv_start = re.compile(r"\.\d+\+\d")
inv_end = re.compile(r"_\d+\+\d")
- if inv_start.search(quibble_input) or inv_end.search(quibble_input):
+ if inv_start.search(input_formatted_variant) or inv_end.search(input_formatted_variant):
cck = True
- if minus.search(quibble_input):
+ if minus.search(input_formatted_variant):
# Regular expression catches the start of the interval only based on .00-00 pattern
inv_start = re.compile(r"\.\d+-\d")
inv_end = re.compile(r"_\d+-\d")
- if inv_start.search(quibble_input) or inv_end.search(quibble_input):
+ if inv_start.search(input_formatted_variant) or inv_end.search(input_formatted_variant):
cck = True
# COORDINATE CHECKER
@@ -493,7 +491,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
if 'del' in formatted_variant:
if out_hgvs_obj.type == 'c':
coding = out_hgvs_obj
- elif quibble_input_hgvs_obj.type == 'c':
+ elif input_hgvs_obj.type == 'c':
coding = validator.coding(out_hgvs_obj)
else:# not actually coding
coding = out_hgvs_obj
@@ -520,7 +518,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
post_var = validator.myevm_g_to_t(variant.evm, pre_var, trans_acc)
except vvhgvs.exceptions.HGVSError as error:
if "Alignment is incomplete" in str(error):
- output = hgvs_utils.incomplete_alignment_mapping_t_to_g(validator, variant)
+ output = hgvs_utils.incomplete_alignment_mapping_t_to_g(validator, variant,out_hgvs_obj)
if output is None:
error = str(error)
variant.warnings.append(error)
@@ -534,7 +532,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
logger.info(str(error))
return True
- test = quibble_input_hgvs_obj
+ test = input_hgvs_obj
if post_var.posedit.pos.start.base != test.posedit.pos.start.base or \
post_var.posedit.pos.end.base != test.posedit.pos.end.base:
@@ -578,7 +576,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
if out_hgvs_obj.type == 'c':
coding = out_hgvs_obj
- elif quibble_input_hgvs_obj.type == 'c':
+ elif input_hgvs_obj.type == 'c':
coding = validator.coding(out_hgvs_obj)
else:# not actually coding
coding = out_hgvs_obj
@@ -594,7 +592,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
except vvhgvs.exceptions.HGVSError as e:
logger.info(f"Error: {str(e)}. Variant: {pre_var}")
if "Alignment is incomplete" in str(e):
- pre_var = hgvs_utils.incomplete_alignment_mapping_t_to_g(validator, variant)
+ pre_var = hgvs_utils.incomplete_alignment_mapping_t_to_g(validator, variant,out_hgvs_obj)
if to_g is None:
error = str(e)
variant.warnings.append(error)
@@ -606,7 +604,7 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
else:
logger.info(f"{pre_var} mapped to {post_var}")
- test = quibble_input_hgvs_obj
+ test = input_hgvs_obj
if post_var.posedit.pos.start.base != test.posedit.pos.start.base or \
post_var.posedit.pos.end.base != test.posedit.pos.end.base:
@@ -646,9 +644,9 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
variant.warnings.extend([caution])
raise MappersError(caution)
- elif ':g.' not in quibble_input:
+ elif ':g.' not in input_formatted_variant:
query = out_hgvs_obj
- test = quibble_input_hgvs_obj
+ test = input_hgvs_obj
if str(query.posedit.pos) != str(test.posedit.pos):
automap = str(test) + ' automapped to ' + str(query)
@@ -656,12 +654,12 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
variant.quibble=out_hgvs_obj
# VALIDATION of intronic variants
- pre_valid = quibble_input_hgvs_obj
+ pre_valid = input_hgvs_obj
post_valid = out_hgvs_obj
# valid is false if the input contains a \d+\d, \d-\d or :g.
if not valid:
- genomic_validation = validator.genomic(quibble_input_hgvs_obj, variant.no_norm_evm, variant.primary_assembly,
+ genomic_validation = validator.genomic(input_hgvs_obj, variant.no_norm_evm, variant.primary_assembly,
variant)
if fn.valstr(pre_valid) != fn.valstr(post_valid):
if variant.reftype != ':g.':
@@ -980,9 +978,12 @@ def transcripts_to_gene(variant, validator, select_transcripts_dict_plus_version
str(updated_transcript_variant) + ' MUST be fully validated prior to '
'use in reports: '
'select_variants=' + fn.valstr(updated_transcript_variant))
- variant.coding = hgvs_coding
- variant.genomic_r = hgvs_refseq
- variant.genomic_g = unset_hgvs_obj_ref(hgvs_genomic)
+
+ if hgvs_coding:
+ print("coding:" + str(hgvs_coding))
+ variant.hgvs_transcript_variant = hgvs_coding
+ variant.hgvs_genomic = hgvs_genomic
+ variant.hgvs_refseqgene_variant = hgvs_refseq
variant.protein = hgvs_protein
return False
@@ -993,14 +994,14 @@ def final_tx_to_multiple_genomic(variant, validator, tx_variant, liftover_level=
try:
tx_variant.ac
- variant.hgvs_coding = tx_variant
+ hgvs_coding = tx_variant
except AttributeError:
- variant.hgvs_coding = validator.hp.parse_hgvs_variant(str(tx_variant))
+ hgvs_coding = validator.hp.parse_hgvs_variant(str(tx_variant))
# Look for variants spanning introns
disable_gap_compensation = False
try:
- variant.hn.normalize(variant.hgvs_coding)
+ variant.hn.normalize(hgvs_coding)
except vvhgvs.exceptions.HGVSUnsupportedOperationError as e:
error = str(e)
if 'boundary' in error or 'spanning' in error:
@@ -1012,12 +1013,11 @@ def final_tx_to_multiple_genomic(variant, validator, tx_variant, liftover_level=
multi_g = []
multi_list = []
- mapping_options = variant.map_dat.mapping_options(variant.hgvs_coding.ac,hdp=validator.hdp)
+ mapping_options = variant.map_dat.mapping_options(hgvs_coding.ac,hdp=validator.hdp)
mapping_options = sorted(mapping_options, key=itemgetter(1))
-
for alt_chr in mapping_options:
if liftover_level is None:
- multi_list.append(variant.genomic_g.ac)
+ multi_list.append(variant.hgvs_genomic.ac)
elif liftover_level == 'primary':
if ('NC_' in alt_chr[1]) and alt_chr[2] == validator.alt_aln_method:
multi_list.append(alt_chr[1])
@@ -1036,22 +1036,21 @@ def final_tx_to_multiple_genomic(variant, validator, tx_variant, liftover_level=
try:
# Re set ori
ori = variant.map_dat.tx_exons(
- variant.hgvs_coding.ac, alt_chr,
+ hgvs_coding.ac, alt_chr,
validator.alt_aln_method,
hdp=validator.hdp)
# Map the variant to the genome
- hgvs_alt_genomic = validator.myvm_t_to_g(variant.hgvs_coding, alt_chr, variant.no_norm_evm,
+ hgvs_alt_genomic = validator.myvm_t_to_g(hgvs_coding, alt_chr, variant.no_norm_evm,
variant.hn, variant.map_dat)
# Loop out gap code under these circumstances!
- if variant.map_dat.is_gapped_map(variant.hgvs_coding.ac,hgvs_alt_genomic.ac,validator):
+ if variant.map_dat.is_gapped_map(hgvs_coding.ac,hgvs_alt_genomic.ac,validator):
# warn on gap_compensation for
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
+ hgvs_alt_genomic, hgvs_coding, ori, alt_chr, rec_var)
+ logger.info(f"gap_compensation_3 done for {hgvs_coding} mapped to {hgvs_alt_genomic}")
logger.info(f"hgvs_coding updated to {hgvs_coding}")
# Check for mismatched sequence in dup variants
@@ -1071,10 +1070,10 @@ def final_tx_to_multiple_genomic(variant, validator, tx_variant, liftover_level=
multi_g.append(hgvs_alt_genomic)
else:
- if hgvs_alt_genomic.posedit.edit.type == variant.hgvs_coding.posedit.edit.type and \
+ if hgvs_alt_genomic.posedit.edit.type == hgvs_coding.posedit.edit.type and \
"ins" not in hgvs_alt_genomic.posedit.edit.type:
try:
- rev_tx = variant.reverse_normalizer.normalize(variant.hgvs_coding)
+ rev_tx = variant.reverse_normalizer.normalize(hgvs_coding)
rev_g = validator.myvm_t_to_g(rev_tx, alt_chr, variant.no_norm_evm,
variant.hn, variant.map_dat)
rev_g = variant.hn.normalize(rev_g)
@@ -1088,7 +1087,7 @@ def final_tx_to_multiple_genomic(variant, validator, tx_variant, liftover_level=
# truly gapped alignment.
except KeyError:
warnings = warnings + ': Suspected incomplete alignment between transcript %s and ' \
- 'genomic reference sequence %s' % (variant.hgvs_coding.ac, alt_chr)
+ 'genomic reference sequence %s' % (hgvs_coding.ac, alt_chr)
except vvhgvs.exceptions.HGVSError as e:
logger.info(str(e))
diff --git a/VariantValidator/modules/use_checking.py b/VariantValidator/modules/use_checking.py
index 868f953c..894c6f49 100644
--- a/VariantValidator/modules/use_checking.py
+++ b/VariantValidator/modules/use_checking.py
@@ -342,80 +342,77 @@ def structure_checks(variant, validator):
Primarily, this code filters out variants that cannot realistically be
auto corrected and will cause the downstream functions to return errors
"""
- if type(variant.quibble) is not str:
- input_parses = copy.deepcopy(variant.quibble)
- else:
- input_parses = validator.hp.parse_hgvs_variant(variant.quibble)
- variant.input_parses = input_parses
- variant.gene_symbol = validator.db.get_gene_symbol_from_transcript_id(variant.input_parses.ac)
+
+ input_parses = copy.deepcopy(variant.quibble)
+ variant.gene_symbol = validator.db.get_gene_symbol_from_transcript_id(input_parses.ac)
if variant.gene_symbol == 'none':
variant.gene_symbol = ''
if input_parses.type == 'g' or input_parses.type == 'm':
- check = structure_checks_g(variant, validator)
+ check = structure_checks_g(variant, validator, input_parses)
if check:
return True
elif input_parses.type == 'c':
- check = structure_checks_c(variant, validator)
+ check = structure_checks_c(variant, validator, input_parses)
if check:
# Also check intron boundaries to provide additional warnings
- if "beyond the bounds" in str(variant.warnings) and (variant.input_parses.posedit.pos.start.offset != 0 or
- variant.input_parses.posedit.pos.end.offset != 0):
- if variant.input_parses.posedit.pos.start.offset != 0:
- variant.input_parses.posedit.pos.start.offset = 1
- if variant.input_parses.posedit.pos.end.offset != 0:
- variant.input_parses.posedit.pos.end.offset =1
- hgvs_genomic_vt = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm,
+ if "beyond the bounds" in str(variant.warnings) and (input_parses.posedit.pos.start.offset != 0 or
+ input_parses.posedit.pos.end.offset != 0):
+ if input_parses.posedit.pos.start.offset != 0:
+ input_parses.posedit.pos.start.offset = 1
+ if input_parses.posedit.pos.end.offset != 0:
+ input_parses.posedit.pos.end.offset =1
+ hgvs_genomic_vt = validator.myevm_t_to_g(input_parses, variant.no_norm_evm,
variant.primary_assembly, variant.hn, variant)
- format_converters.remap_intronic(variant.input_parses, hgvs_genomic_vt, variant, validator)
+ format_converters.remap_intronic(input_parses, hgvs_genomic_vt, variant, validator)
return True
elif input_parses.type == 'n':
- check = structure_checks_n(variant, validator)
+ check = structure_checks_n(variant, validator, input_parses)
if check:
return True
else:
pass
-def structure_checks_g(variant, validator):
+def structure_checks_g(variant, validator, input_parses):
"""
Structure checks for when reftype is genomic
"""
- if variant.input_parses.ac[:3] not in ['NC_', 'NG_', 'NT_', 'NW_']:
- error = 'Invalid reference sequence identifier (' + variant.input_parses.ac + ')'
+ if input_parses.ac[:3] not in ['NC_', 'NG_', 'NT_', 'NW_']:
+ error = 'Invalid reference sequence identifier (' + input_parses.ac + ')'
variant.warnings.append(error)
logger.info(error)
return True
try:
- validator.vr.validate(variant.input_parses)
+ validator.vr.validate(input_parses)
except Exception as e:
if "does not agree with reference sequence ()" in str(e):
- e = "The specified coordinate is outside the boundaries of reference sequence %s " % variant.input_parses.ac
+ e = "The specified coordinate is outside the boundaries of reference sequence %s " % input_parses.ac
if "base start position must be <= end position" in str(e) and (
- "NC_012920.1" in variant.hgvs_formatted.ac or
- "NC_001807.4" in variant.hgvs_formatted.ac):
+ "NC_012920.1" in variant.quibble.ac or
+ "NC_001807.4" in variant.quibble.ac):
- if variant.hgvs_formatted.ac not in variant.original:
+ if variant.quibble.ac not in variant.original:
err = "This is not a valid HGVS variant description, because no reference sequence ID has " \
- "been provided, instead use %s" % str(variant.hgvs_formatted)
+ "been provided, instead use %s" % str(variant.quibble)
variant.warnings.append(err)
variant.warnings.append("The variant positions are valid but we cannot normalize variants spanning "
"the origin of circular reference sequences")
return True
- elif "insertion length must be 1" in str(e) and "(" in str(variant.input_parses.posedit.pos) and ")" in \
- str(variant.input_parses.posedit.pos):
+ elif "insertion length must be 1" in str(e) and "(" in str(input_parses.posedit.pos) and ")" in \
+ str(input_parses.posedit.pos):
return True
- elif "insertion length must be 1" in str(e) and "(" not in str(variant.input_parses.posedit.pos) and ")" not in\
- str(variant.input_parses.posedit.pos):
+ elif "insertion length must be 1" in str(e) and "(" not in str(input_parses.posedit.pos) and ")" not in\
+ str(input_parses.posedit.pos):
ins_warning = (f'Insertion length must be 1 e.g. '
- f'{str(int(variant.input_parses.posedit.pos.start.base))}'
- f'_{str(int(variant.input_parses.posedit.pos.start.base)+1)}'
- f'ins{variant.input_parses.posedit.edit.alt}')
+ f'{str(int(input_parses.posedit.pos.start.base))}'
+ f'_{str(int(input_parses.posedit.pos.start.base)+1)}'
+ f'ins{input_parses.posedit.edit.alt}')
variant.warnings.append(ins_warning)
for warning in variant.warnings:
if warning == "insertion length must be 1":
@@ -424,7 +421,7 @@ def structure_checks_g(variant, validator):
return True
elif "Length implied by coordinates must equal sequence deletion length" in str(e) and \
- "(" in str(variant.input_parses.posedit.pos) and ")" in str(variant.input_parses.posedit.pos):
+ "(" in str(input_parses.posedit.pos) and ")" in str(input_parses.posedit.pos):
return True
else:
error = str(e)
@@ -434,7 +431,7 @@ def structure_checks_g(variant, validator):
# Additional test
try:
- np = variant.hn.normalize(variant.input_parses)
+ np = variant.hn.normalize(input_parses)
except vvhgvs.exceptions.HGVSError as e:
error = str(e)
variant.warnings.append(error)
@@ -443,8 +440,8 @@ def structure_checks_g(variant, validator):
# Look for variants in runs of N bases
try:
- if "N" in variant.input_parses.posedit.edit.ref:
- error = (f"UncertainSequenceError: The submitted variant description {variant.input_parses} refers to a "
+ if "N" in input_parses.posedit.edit.ref:
+ error = (f"UncertainSequenceError: The submitted variant description {input_parses} refers to a "
f"genomic reference region with "
f"an uncertain base composition (N)")
variant.warnings.append(error)
@@ -456,7 +453,7 @@ def structure_checks_g(variant, validator):
return False
-def structure_checks_c(variant, validator):
+def structure_checks_c(variant, validator, input_parses):
"""
structure checks for when reftype is coding
:param variant:
@@ -464,18 +461,18 @@ def structure_checks_c(variant, validator):
:return:
"""
- if '*' in str(variant.input_parses) or 'c.-' in str(variant.input_parses):
+ if '*' in str(input_parses) or 'c.-' in str(input_parses):
# Catch variation in UTRs
# These should be in the sequence so can be directly validated. Need to pass to n.
try:
- validator.vr.validate(variant.input_parses)
+ validator.vr.validate(input_parses)
except vvhgvs.exceptions.HGVSError as e:
error = str(e)
if 'datums is ill-defined' in error:
- called_ref = variant.input_parses.posedit.edit.ref
+ called_ref = input_parses.posedit.edit.ref
try:
- to_n = variant.evm.c_to_n(variant.input_parses)
+ to_n = variant.evm.c_to_n(input_parses)
except vvhgvs.exceptions.HGVSInvalidVariantError as e:
error = str(e)
variant.warnings.append(error)
@@ -490,61 +487,61 @@ def structure_checks_c(variant, validator):
logger.info(error)
return True
else:
- if variant.input_parses.posedit.edit.type == "ins":
- variant.input_parses.posedit.edit.ref = None
+ if input_parses.posedit.edit.type == "ins":
+ input_parses.posedit.edit.ref = None
else:
- variant.input_parses.posedit.edit.ref = ''
- variant.hgvs_formatted = variant.input_parses
+ input_parses.posedit.edit.ref = ''
+ variant.quibble = input_parses
else:
if 'bounds' in error or 'intronic variant' in error:
try:
- variant.hn.normalize(variant.input_parses)
+ variant.hn.normalize(input_parses)
except vvhgvs.exceptions.HGVSError as e:
logger.debug("Except passed, %s", e)
if 'bounds' in error:
try:
- identity_info = validator.hdp.get_tx_identity_info(variant.input_parses.ac)
+ identity_info = validator.hdp.get_tx_identity_info(input_parses.ac)
ref_start = identity_info[3]
ref_end = identity_info[4]
- if '-' in str(variant.input_parses.posedit.pos.start) and \
- variant.input_parses.posedit.pos.start.offset == 0:
+ if '-' in str(input_parses.posedit.pos.start) and \
+ input_parses.posedit.pos.start.offset == 0:
# upstream positions
boundary = -ref_start
- remainder = variant.input_parses.posedit.pos.start.base - boundary
- variant.input_parses.posedit.pos.start.base = boundary
- variant.input_parses.posedit.pos.start.offset = remainder
- if '-' in str(variant.input_parses.posedit.pos.end) and \
- variant.input_parses.posedit.pos.end.offset == 0:
+ remainder = input_parses.posedit.pos.start.base - boundary
+ input_parses.posedit.pos.start.base = boundary
+ input_parses.posedit.pos.start.offset = remainder
+ if '-' in str(input_parses.posedit.pos.end) and \
+ input_parses.posedit.pos.end.offset == 0:
boundary = -ref_start
- remainder = variant.input_parses.posedit.pos.end.base - boundary
- variant.input_parses.posedit.pos.end.base = boundary
- variant.input_parses.posedit.pos.end.offset = remainder
- if '*' in str(variant.input_parses.posedit.pos.start) and \
- variant.input_parses.posedit.pos.start.offset == 0:
+ remainder = input_parses.posedit.pos.end.base - boundary
+ input_parses.posedit.pos.end.base = boundary
+ input_parses.posedit.pos.end.offset = remainder
+ if '*' in str(input_parses.posedit.pos.start) and \
+ input_parses.posedit.pos.start.offset == 0:
# downstream positions
- tot_end_pos = str(variant.input_parses.posedit.pos.start).replace('*', '')
- ts_seq = validator.sf.fetch_seq(variant.input_parses.ac)
+ tot_end_pos = str(input_parses.posedit.pos.start).replace('*', '')
+ ts_seq = validator.sf.fetch_seq(input_parses.ac)
boundary = len(ts_seq) - ref_end
- variant.input_parses.posedit.pos.start.base = boundary
+ input_parses.posedit.pos.start.base = boundary
offset = int(tot_end_pos) - boundary
- variant.input_parses.posedit.pos.start.offset = offset
- if '*' in str(variant.input_parses.posedit.pos.end) and \
- variant.input_parses.posedit.pos.end.offset == 0:
- tot_end_pos = str(variant.input_parses.posedit.pos.end).replace('*', '')
- ts_seq = validator.sf.fetch_seq(variant.input_parses.ac)
+ input_parses.posedit.pos.start.offset = offset
+ if '*' in str(input_parses.posedit.pos.end) and \
+ input_parses.posedit.pos.end.offset == 0:
+ tot_end_pos = str(input_parses.posedit.pos.end).replace('*', '')
+ ts_seq = validator.sf.fetch_seq(input_parses.ac)
boundary = len(ts_seq) - ref_end
- variant.input_parses.posedit.pos.end.base = boundary
+ input_parses.posedit.pos.end.base = boundary
offset = int(tot_end_pos) - boundary
- variant.input_parses.posedit.pos.end.offset = offset
+ input_parses.posedit.pos.end.offset = offset
# Create a lose vm instance
variant.lose_vm = vvhgvs.variantmapper.VariantMapper(validator.hdp,
replace_reference=True,
prevalidation_level=None)
- report_gen = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm,
+ report_gen = validator.myevm_t_to_g(input_parses, variant.no_norm_evm,
variant.primary_assembly, variant.hn, variant)
report_gen = variant.hn.normalize(report_gen)
error = 'Using a transcript reference sequence to specify a variant position that lies ' \
@@ -557,15 +554,15 @@ def structure_checks_c(variant, validator):
return True
try:
- variant.input_parses = variant.evm.c_to_n(variant.input_parses)
+ input_parses = variant.evm.c_to_n(input_parses)
except vvhgvs.exceptions.HGVSError as e:
error = str(e)
variant.warnings.append(error)
logger.info(e)
return True
- if 'n.1-' in str(variant.input_parses):
- input_parses = variant.evm.n_to_c(variant.input_parses)
+ if 'n.1-' in str(input_parses):
+ input_parses = variant.evm.n_to_c(input_parses)
error = 'Using a transcript reference sequence to specify a variant position that lies outside of the ' \
'reference sequence is not HGVS-compliant. Instead re-submit '
genomic_position = validator.myevm_t_to_g(input_parses, variant.no_norm_evm, variant.primary_assembly,
@@ -577,54 +574,54 @@ def structure_checks_c(variant, validator):
return True
# Re-map input_parses back to c. variant
- variant.input_parses = variant.evm.n_to_c(variant.input_parses)
+ input_parses = variant.evm.n_to_c(input_parses)
# Intronic positions in UTRs
- if re.search(r'\d-\d', str(variant.input_parses)) or re.search(r'\d\+\d', str(variant.input_parses)):
+ if re.search(r'\d-\d', str(input_parses)) or re.search(r'\d\+\d', str(input_parses)):
# Can we go c-g-c
try:
- to_genome = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm,
+ to_genome = validator.myevm_t_to_g(input_parses, variant.no_norm_evm,
variant.primary_assembly, variant.hn, variant)
- to_tx = variant.evm.g_to_t(to_genome, variant.input_parses.ac)
+ to_tx = variant.evm.g_to_t(to_genome, input_parses.ac)
except vvhgvs.exceptions.HGVSInvalidIntervalError as e:
error = str(e)
if 'bounds' in error:
try:
- identity_info = validator.hdp.get_tx_identity_info(variant.input_parses.ac)
+ identity_info = validator.hdp.get_tx_identity_info(input_parses.ac)
ref_start = identity_info[3]
ref_end = identity_info[4]
- if '-' in str(variant.input_parses.posedit.pos.start):
+ if '-' in str(input_parses.posedit.pos.start):
# upstream positions
boundary = -ref_start
- remainder = variant.input_parses.posedit.pos.start.base - boundary
- variant.input_parses.posedit.pos.start.base = boundary
- variant.input_parses.posedit.pos.start.offset = remainder
- if '-' in str(variant.input_parses.posedit.pos.end):
+ remainder = input_parses.posedit.pos.start.base - boundary
+ input_parses.posedit.pos.start.base = boundary
+ input_parses.posedit.pos.start.offset = remainder
+ if '-' in str(input_parses.posedit.pos.end):
boundary = -ref_start
- remainder = variant.input_parses.posedit.pos.end.base - boundary
- variant.input_parses.posedit.pos.end.base = boundary
- variant.input_parses.posedit.pos.end.offset = remainder
- if '*' in str(variant.input_parses.posedit.pos.start):
+ remainder = input_parses.posedit.pos.end.base - boundary
+ input_parses.posedit.pos.end.base = boundary
+ input_parses.posedit.pos.end.offset = remainder
+ if '*' in str(input_parses.posedit.pos.start):
# downstream positions
- tot_end_pos = str(variant.input_parses.posedit.pos.start).replace('*', '')
- ts_seq = validator.sf.fetch_seq(variant.input_parses.ac)
+ tot_end_pos = str(input_parses.posedit.pos.start).replace('*', '')
+ ts_seq = validator.sf.fetch_seq(input_parses.ac)
boundary = len(ts_seq) - ref_end
- variant.input_parses.posedit.pos.start.base = boundary
+ input_parses.posedit.pos.start.base = boundary
te1, te2 = tot_end_pos.split('+')
tot_end_pos = int(te1) + int(te2)
offset = tot_end_pos - boundary
- variant.input_parses.posedit.pos.start.offset = offset
- if '*' in str(variant.input_parses.posedit.pos.end):
- tot_end_pos = str(variant.input_parses.posedit.pos.end).replace('*', '')
- ts_seq = validator.sf.fetch_seq(variant.input_parses.ac)
+ input_parses.posedit.pos.start.offset = offset
+ if '*' in str(input_parses.posedit.pos.end):
+ tot_end_pos = str(input_parses.posedit.pos.end).replace('*', '')
+ ts_seq = validator.sf.fetch_seq(input_parses.ac)
boundary = len(ts_seq) - ref_end
- variant.input_parses.posedit.pos.end.base = boundary
+ input_parses.posedit.pos.end.base = boundary
te1, te2 = tot_end_pos.split('+')
tot_end_pos = int(te1) + int(te2)
offset = tot_end_pos - boundary
- variant.input_parses.posedit.pos.end.offset = offset
+ input_parses.posedit.pos.end.offset = offset
- report_gen = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm,
+ report_gen = validator.myevm_t_to_g(input_parses, variant.no_norm_evm,
variant.primary_assembly, variant.hn, variant)
report_gen = variant.hn.normalize(report_gen)
error = 'Using a transcript reference sequence to specify a variant position that lies ' \
@@ -647,14 +644,14 @@ def structure_checks_c(variant, validator):
continue
gens.append(el_l[-1])
acs = '; '.join(gens)
- error = 'Cannot map ' + fn.valstr(variant.input_parses) + ' to a genomic position. '\
- + variant.input_parses.ac + ' can only be partially aligned to genomic reference ' \
+ error = 'Cannot map ' + fn.valstr(input_parses) + ' to a genomic position. '\
+ + input_parses.ac + ' can only be partially aligned to genomic reference ' \
'sequences ' + acs
variant.warnings.append(error)
logger.info(error)
return True
- elif re.search(r'\d-', str(variant.input_parses)) or re.search(r'\d\+', str(variant.input_parses)):
+ elif re.search(r'\d-', str(input_parses)) or re.search(r'\d\+', str(input_parses)):
# Create a no_replace vm instance
variant.no_replace_vm = vvhgvs.variantmapper.VariantMapper(validator.hdp,
@@ -663,12 +660,12 @@ def structure_checks_c(variant, validator):
# Quick look at syntax validation
try:
- validator.vr.validate(variant.input_parses)
+ validator.vr.validate(input_parses)
except vvhgvs.exceptions.HGVSInvalidVariantError as e:
error = str(e)
if 'bounds' in error:
try:
- report_gen = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm,
+ report_gen = validator.myevm_t_to_g(input_parses, variant.no_norm_evm,
variant.primary_assembly, variant.hn, variant)
report_gen = variant.hn.normalize(report_gen)
except vvhgvs.exceptions.HGVSError as e:
@@ -680,18 +677,18 @@ def structure_checks_c(variant, validator):
logger.info(error)
return True
elif 'insertion length must be 1' in error:
- if "-" in str(variant.input_parses.posedit.pos.start.offset):
- start_offset = str(variant.input_parses.posedit.pos.start.offset - 1)
- end_offset = str(variant.input_parses.posedit.pos.start.offset)
- elif ("-" not in str(variant.input_parses.posedit.pos.start.offset) and
- variant.input_parses.posedit.pos.start.offset != 0):
- start_offset = f"+{str(variant.input_parses.posedit.pos.start.offset)}"
- end_offset = f"+{str(variant.input_parses.posedit.pos.start.offset + 1)}"
- if "(" not in str(variant.input_parses.posedit.pos):
+ if "-" in str(input_parses.posedit.pos.start.offset):
+ start_offset = str(input_parses.posedit.pos.start.offset - 1)
+ end_offset = str(input_parses.posedit.pos.start.offset)
+ elif ("-" not in str(input_parses.posedit.pos.start.offset) and
+ input_parses.posedit.pos.start.offset != 0):
+ start_offset = f"+{str(input_parses.posedit.pos.start.offset)}"
+ end_offset = f"+{str(input_parses.posedit.pos.start.offset + 1)}"
+ if "(" not in str(input_parses.posedit.pos):
ins_warning = (f'Insertion length must be 1 e.g. '
- f'{variant.input_parses.posedit.pos.start.base}{start_offset}'
- f'_{str(int(variant.input_parses.posedit.pos.start.base))}{end_offset}'
- f'ins{variant.input_parses.posedit.edit.alt}')
+ f'{input_parses.posedit.pos.start.base}{start_offset}'
+ f'_{str(int(input_parses.posedit.pos.start.base))}{end_offset}'
+ f'ins{input_parses.posedit.edit.alt}')
variant.warnings.append(ins_warning)
for warning in variant.warnings:
if warning == "insertion length must be 1":
@@ -699,9 +696,9 @@ def structure_checks_c(variant, validator):
return True
elif 'base start position must be <= end position' in error:
- correction = copy.deepcopy(variant.input_parses)
- st = variant.input_parses.posedit.pos.start
- ed = variant.input_parses.posedit.pos.end
+ correction = copy.deepcopy(input_parses)
+ st = input_parses.posedit.pos.start
+ ed = input_parses.posedit.pos.end
correction.posedit.pos.start = ed
correction.posedit.pos.end = st
error = error + ': Did you mean ' + str(correction) + '?'
@@ -713,34 +710,34 @@ def structure_checks_c(variant, validator):
# Have to use this method due to potential multi chromosome error, note normalizes but does not replace sequence
output = None
try:
- output = validator.noreplace_myevm_t_to_g(variant.input_parses, variant)
+ output = validator.noreplace_myevm_t_to_g(input_parses, variant)
except vvhgvs.exceptions.HGVSDataNotAvailableError:
- tx_info = validator.hdp.get_tx_identity_info(variant.input_parses.ac)
- if (variant.input_parses.posedit.pos.end.base > int(tx_info[4]) or variant.input_parses.posedit.pos.end.base
- > int(tx_info[4])) and ("*" not in str(variant.input_parses.posedit.pos.end) or "*" not in
- str(variant.input_parses.posedit.pos.start)):
+ tx_info = validator.hdp.get_tx_identity_info(input_parses.ac)
+ if (input_parses.posedit.pos.end.base > int(tx_info[4]) or input_parses.posedit.pos.end.base
+ > int(tx_info[4])) and ("*" not in str(input_parses.posedit.pos.end) or "*" not in
+ str(input_parses.posedit.pos.start)):
errors = ["CDSError: Variant start position and/or end position are beyond the CDS end position "
"and likely also beyond the end of the selected reference sequence"]
else:
- errors = ['Required information for ' + variant.input_parses.ac + ' is missing from the Universal '
+ errors = ['Required information for ' + input_parses.ac + ' is missing from the Universal '
'Transcript Archive', 'Query gene2transcripts with search term %s for '
- 'available transcripts' % variant.input_parses.ac.split('.')[0]]
+ 'available transcripts' % input_parses.ac.split('.')[0]]
variant.warnings.extend(errors)
logger.info(str(errors))
return True
except ValueError as e:
error = str(e)
if '> end' in error:
- error = 'Interval start position ' + str(variant.input_parses.posedit.pos.start) + ' > interval end '\
- 'position ' + str(variant.input_parses.posedit.pos.end)
+ error = 'Interval start position ' + str(input_parses.posedit.pos.start) + ' > interval end '\
+ 'position ' + str(input_parses.posedit.pos.end)
variant.warnings.append(error)
logger.info(error)
return True
except vvhgvs.exceptions.HGVSInvalidVariantError as e:
error = str(e)
if 'base start position must be <= end position' in error:
- error = 'Interval start position ' + str(variant.input_parses.posedit.pos.start) + ' > interval end' \
- ' position ' + str(variant.input_parses.posedit.pos.end)
+ error = 'Interval start position ' + str(input_parses.posedit.pos.start) + ' > interval end' \
+ ' position ' + str(input_parses.posedit.pos.end)
variant.warnings.append(error)
logger.info(error)
return True
@@ -750,10 +747,10 @@ def structure_checks_c(variant, validator):
return True
try:
- variant.evm.g_to_t(output, variant.input_parses.ac)
+ variant.evm.g_to_t(output, input_parses.ac)
except vvhgvs.exceptions.HGVSError as e:
if "Alignment is incomplete" in str(e):
- output = hgvs_utils.incomplete_alignment_mapping_t_to_g(validator, variant)
+ output = hgvs_utils.incomplete_alignment_mapping_t_to_g(validator, variant,input_parses)
if output is None:
error = str(e)
variant.warnings.append(error)
@@ -766,14 +763,14 @@ def structure_checks_c(variant, validator):
return True
# Check that the reference is correct by direct mapping without replacing reference
- check_ref_g = variant.no_replace_vm.t_to_g(variant.input_parses, output.ac,
+ check_ref_g = variant.no_replace_vm.t_to_g(input_parses, output.ac,
alt_aln_method=validator.alt_aln_method)
- check_ref_t = variant.no_replace_vm.g_to_t(check_ref_g, variant.input_parses.ac,
+ check_ref_t = variant.no_replace_vm.g_to_t(check_ref_g, input_parses.ac,
alt_aln_method=validator.alt_aln_method)
# Snapshot current variant error log
- if "*" in str(check_ref_t) and "*" not in str(variant.input_parses):
- convert = "%s auto-mapped to %s" % (variant.input_parses, check_ref_t)
+ if "*" in str(check_ref_t) and "*" not in str(input_parses):
+ convert = "%s auto-mapped to %s" % (input_parses, check_ref_t)
variant.warnings.append(convert)
snap = copy.copy(variant.warnings)
@@ -800,24 +797,24 @@ def structure_checks_c(variant, validator):
else:
# All other variation
try:
- validator.vr.validate(variant.input_parses)
+ validator.vr.validate(input_parses)
except vvhgvs.exceptions.HGVSUnsupportedOperationError as e:
logger.debug("Except passed, %s", e)
except vvhgvs.exceptions.HGVSInvalidVariantError as e:
error = str(e)
# This catches errors in introns
if 'base start position must be <= end position' in error:
- error = 'Interval start position ' + str(variant.input_parses.posedit.pos.start) + ' > interval end '\
- 'position ' + str(variant.input_parses.posedit.pos.end)
- if "(" not in str(variant.input_parses.posedit.pos):
+ error = 'Interval start position ' + str(input_parses.posedit.pos.start) + ' > interval end '\
+ 'position ' + str(input_parses.posedit.pos.end)
+ if "(" not in str(input_parses.posedit.pos):
variant.warnings.append(error)
logger.info(error)
if 'insertion length must be 1' in error:
- if "(" not in str(variant.input_parses.posedit.pos):
+ if "(" not in str(input_parses.posedit.pos):
ins_warning = (f'Insertion length must be 1 e.g. '
- f'{str(int(variant.input_parses.posedit.pos.start.base))}'
- f'_{str(int(variant.input_parses.posedit.pos.start.base)+1)}'
- f'ins{variant.input_parses.posedit.edit.alt}')
+ f'{str(int(input_parses.posedit.pos.start.base))}'
+ f'_{str(int(input_parses.posedit.pos.start.base)+1)}'
+ f'ins{input_parses.posedit.edit.alt}')
variant.warnings.append(ins_warning)
for warning in variant.warnings:
if warning == "insertion length must be 1":
@@ -832,7 +829,7 @@ def structure_checks_c(variant, validator):
except vvhgvs.exceptions.HGVSError as e:
error = str(e)
if 'bounds' in error:
- error += ' (' + variant.input_parses.ac + ')'
+ error += ' (' + input_parses.ac + ')'
variant.warnings.append(error)
logger.info(error)
return True
@@ -840,25 +837,25 @@ def structure_checks_c(variant, validator):
return False
-def structure_checks_n(variant, validator):
+def structure_checks_n(variant, validator, input_parses):
"""
structure checks for reftype nucleotide
:param variant:
:param validator:
:return:
"""
- if '+' in str(variant.input_parses) or '-' in str(variant.input_parses):
+ if '+' in str(input_parses) or '-' in str(input_parses):
# Catch variation in UTRs
# These should be in the sequence so can be directly validated. Need to pass to n.
try:
- validator.vr.validate(variant.input_parses)
+ validator.vr.validate(input_parses)
except vvhgvs.exceptions.HGVSError as e:
error = str(e)
if 'intronic variant' in error:
pass
elif 'datums is ill-defined' in error:
- called_ref = variant.input_parses.posedit.edit.ref
- to_n = variant.evm.c_to_n(variant.input_parses)
+ called_ref = input_parses.posedit.edit.ref
+ to_n = variant.evm.c_to_n(input_parses)
actual_ref = to_n.posedit.edit.ref
if called_ref != actual_ref:
error = 'Variant reference (' + called_ref + ') does not agree with reference sequence (' + \
@@ -867,27 +864,27 @@ def structure_checks_n(variant, validator):
logger.info(str(error))
return True
else:
- variant.input_parses.posedit.edit.ref = ''
- variant.hgvs_formatted = variant.input_parses
+ input_parses.posedit.edit.ref = ''
+ variant.quibble = input_parses
elif 'base must be >=1 for datum = SEQ_START or CDS_END' in error:
error = 'The given coordinate is outside the bounds of the reference sequence.'
try:
- if '-' in str(variant.input_parses.posedit.pos.start):
+ if '-' in str(input_parses.posedit.pos.start):
# upstream positions
boundary = 1
- remainder = variant.input_parses.posedit.pos.start.base - boundary
+ remainder = input_parses.posedit.pos.start.base - boundary
remainder = remainder + 1
- variant.input_parses.posedit.pos.start.base = boundary
- variant.input_parses.posedit.pos.start.offset = remainder
- if '-' in str(variant.input_parses.posedit.pos.end):
+ input_parses.posedit.pos.start.base = boundary
+ input_parses.posedit.pos.start.offset = remainder
+ if '-' in str(input_parses.posedit.pos.end):
boundary = 1
- remainder = variant.input_parses.posedit.pos.end.base - boundary
+ remainder = input_parses.posedit.pos.end.base - boundary
remainder = remainder + 1
- variant.input_parses.posedit.pos.end.base = boundary
- variant.input_parses.posedit.pos.end.offset = remainder
- report_gen = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm,
+ input_parses.posedit.pos.end.base = boundary
+ input_parses.posedit.pos.end.offset = remainder
+ report_gen = validator.myevm_t_to_g(input_parses, variant.no_norm_evm,
variant.primary_assembly, variant.hn, variant)
report_gen = variant.hn.normalize(report_gen)
error = 'Using a transcript reference sequence to specify a variant position that lies outside of' \
@@ -902,10 +899,10 @@ def structure_checks_n(variant, validator):
logger.info(error)
return True
- if 'n.1-' in str(variant.input_parses):
+ if 'n.1-' in str(input_parses):
error = 'Using a transcript reference sequence to specify a variant position that lies outside of the ' \
'reference sequence is not HGVS-compliant. Instead re-submit '
- genomic_position = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm, variant.primary_assembly,
+ genomic_position = validator.myevm_t_to_g(input_parses, variant.no_norm_evm, variant.primary_assembly,
variant.hn,variant)
genomic_position = variant.hn.normalize(genomic_position)
error = error + fn.valstr(genomic_position)
@@ -913,15 +910,15 @@ def structure_checks_n(variant, validator):
logger.info(error)
return True
- if re.search(r'\d-', str(variant.input_parses)) or re.search(r'\d\+', str(variant.input_parses)):
+ if re.search(r'\d-', str(input_parses)) or re.search(r'\d\+', str(input_parses)):
# Quick look at syntax validation
try:
- validator.vr.validate(variant.input_parses)
+ validator.vr.validate(input_parses)
except vvhgvs.exceptions.HGVSInvalidVariantError as e:
error = str(e)
if 'bounds' in error:
try:
- report_gen = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm,
+ report_gen = validator.myevm_t_to_g(input_parses, variant.no_norm_evm,
variant.primary_assembly, variant.hn,variant)
report_gen = variant.hn.normalize(report_gen)
except vvhgvs.exceptions.HGVSError as e:
@@ -937,9 +934,9 @@ def structure_checks_n(variant, validator):
logger.info(error)
return True
elif 'base start position must be <= end position' in error:
- correction = copy.deepcopy(variant.input_parses)
- st = variant.input_parses.posedit.pos.start
- ed = variant.input_parses.posedit.pos.end
+ correction = copy.deepcopy(input_parses)
+ st = input_parses.posedit.pos.start
+ ed = input_parses.posedit.pos.end
correction.posedit.pos.start = ed
correction.posedit.pos.end = st
error = error + ': Did you mean ' + str(correction) + '?'
@@ -950,13 +947,13 @@ def structure_checks_n(variant, validator):
return True
elif 'Cannot validate sequence of an intronic variant' in error:
try:
- test_g = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm, variant.primary_assembly,
+ test_g = validator.myevm_t_to_g(input_parses, variant.no_norm_evm, variant.primary_assembly,
variant.hn,variant)
- variant.evm.g_to_t(test_g, variant.input_parses.ac)
+ variant.evm.g_to_t(test_g, input_parses.ac)
except vvhgvs.exceptions.HGVSError as e:
error = str(e)
if 'bounds' in error:
- report_gen = validator.myevm_t_to_g(variant.input_parses, variant.no_norm_evm,
+ report_gen = validator.myevm_t_to_g(input_parses, variant.no_norm_evm,
variant.primary_assembly, variant.hn,variant)
report_gen = variant.hn.normalize(report_gen)
error = 'Using a transcript reference sequence to specify a variant position that lies ' \
@@ -971,12 +968,12 @@ def structure_checks_n(variant, validator):
# sequence
output = None
try:
- output = validator.noreplace_myevm_t_to_g(variant.input_parses, variant)
+ output = validator.noreplace_myevm_t_to_g(input_parses, variant)
except vvhgvs.exceptions.HGVSDataNotAvailableError:
- errors = ['Required information for ' + variant.input_parses.ac + ' is missing from the Universal '
+ errors = ['Required information for ' + input_parses.ac + ' is missing from the Universal '
'Transcript Archive',
'Query gene2transcripts with search term %s for '
- 'available transcripts' % variant.input_parses.ac.split('.')[0]]
+ 'available transcripts' % input_parses.ac.split('.')[0]]
variant.warnings.extend(errors)
logger.info(str(errors))
return True
@@ -984,23 +981,23 @@ def structure_checks_n(variant, validator):
error = str(e)
if '> end' in error:
error = 'Interval start position ' + str(
- variant.input_parses.posedit.pos.start) + ' > interval end position ' + str(
- variant.input_parses.posedit.pos.end)
+ input_parses.posedit.pos.start) + ' > interval end position ' + str(
+ input_parses.posedit.pos.end)
variant.warnings.append(error)
logger.info(error)
return True
except vvhgvs.exceptions.HGVSInvalidVariantError as e:
error = str(e)
if 'base start position must be <= end position' in error:
- correction = copy.deepcopy(variant.input_parses)
- st = variant.input_parses.posedit.pos.start
- ed = variant.input_parses.posedit.pos.end
+ correction = copy.deepcopy(input_parses)
+ st = input_parses.posedit.pos.start
+ ed = input_parses.posedit.pos.end
correction.posedit.pos.start = ed
correction.posedit.pos.end = st
# error = error + ': Did you mean ' + str(correction) + '?'
error = 'Interval start position ' + str(
- variant.input_parses.posedit.pos.start) + ' > interval end position ' + str(
- variant.input_parses.posedit.pos.end)
+ input_parses.posedit.pos.start) + ' > interval end position ' + str(
+ input_parses.posedit.pos.end)
variant.warnings.append(error)
logger.info(error)
return True
@@ -1015,7 +1012,7 @@ def structure_checks_n(variant, validator):
else:
# All other variation
try:
- validator.vr.validate(variant.input_parses)
+ validator.vr.validate(input_parses)
except vvhgvs.exceptions.HGVSUnsupportedOperationError as e:
logger.debug("Except passed, %s", e)
except vvhgvs.exceptions.HGVSInvalidVariantError as e:
@@ -1039,15 +1036,15 @@ def structure_checks_n(variant, validator):
# continue
# This catches errors in introns
if 'base start position must be <= end position' in error:
- # correction = copy.deepcopy(variant.input_parses)
- # st = variant.input_parses.posedit.pos.start
- # ed = variant.input_parses.posedit.pos.end
+ # correction = copy.deepcopy(input_parses)
+ # st = input_parses.posedit.pos.start
+ # ed = input_parses.posedit.pos.end
# correction.posedit.pos.start = ed
# correction.posedit.pos.end = st
# error = error + ': Did you mean ' + str(correction) + '?'
error = 'Interval start position ' + str(
- variant.input_parses.posedit.pos.start) + ' > interval end position ' + str(
- variant.input_parses.posedit.pos.end)
+ input_parses.posedit.pos.start) + ' > interval end position ' + str(
+ input_parses.posedit.pos.end)
logger.info(error)
variant.warnings.append(error)
return True
@@ -1062,7 +1059,7 @@ def structure_checks_n(variant, validator):
except vvhgvs.exceptions.HGVSError as e:
error = str(e)
if 'bounds' in error:
- error = error + ' (' + variant.input_parses.ac + ')'
+ error = error + ' (' + input_parses.ac + ')'
variant.warnings.append(error)
logger.info(error)
return True
diff --git a/VariantValidator/modules/valoutput.py b/VariantValidator/modules/valoutput.py
index 3eb95063..2c2ba131 100644
--- a/VariantValidator/modules/valoutput.py
+++ b/VariantValidator/modules/valoutput.py
@@ -1,6 +1,7 @@
import logging
import json
from VariantValidator.modules import lovd_api
+from VariantValidator.modules.vrs_utils import HGVS_to_VRS
logger = logging.getLogger(__name__)
@@ -35,8 +36,11 @@ def format_as_dict(self, with_meta=True, test=False):
if variant.is_obsolete() and variant.hgvs_transcript_variant == '':
validation_obsolete_counter += 1
identification_key = 'obsolete_record_%s' % validation_obsolete_counter
+ elif variant.hgvs_transcript_variant:
+ identification_key = variant.hgvs_transcript_variant.format({
+ 'max_ref_length': 0})
else:
- identification_key = str(variant.hgvs_transcript_variant)
+ identification_key = 'None'
validation_output[identification_key] = variant.output_dict(test=test)
@@ -121,19 +125,22 @@ def format_as_table(self, with_meta=True):
prot = ''
if variant.hgvs_predicted_protein_consequence is not None:
- prot = variant.hgvs_predicted_protein_consequence['tlr']
+ prot = variant.hgvs_predicted_protein_consequence['prot'].format({
+ 'max_ref_length': 0})
if variant.rna_data is not None:
prot = variant.rna_data["translation"]
grch37 = ''
grch37_vcf = {'chr': '', 'pos': '', 'ref': '', 'alt': '', 'id': ''}
if variant.primary_assembly_loci and 'grch37' in variant.primary_assembly_loci:
- grch37 = variant.primary_assembly_loci['grch37']['hgvs_genomic_description']
+ grch37 = variant.primary_assembly_loci['grch37']['hgvs_genomic_description'].format({
+ 'max_ref_length': 0})
grch37_vcf = variant.primary_assembly_loci['grch37']['vcf']
grch37_vcf['id'] = '.'
grch38 = ''
grch38_vcf = {'chr': '', 'pos': '', 'ref': '', 'alt': '', 'id': ''}
if variant.primary_assembly_loci and 'grch38' in variant.primary_assembly_loci:
- grch38 = variant.primary_assembly_loci['grch38']['hgvs_genomic_description']
+ grch38 = variant.primary_assembly_loci['grch38']['hgvs_genomic_description'].format({
+ 'max_ref_length': 0})
grch38_vcf = variant.primary_assembly_loci['grch38']['vcf']
grch38_vcf['id'] = '.'
alt_genomic = []
@@ -141,7 +148,7 @@ def format_as_table(self, with_meta=True):
for alt in variant.alt_genomic_loci:
for k, v in alt.items():
if k == 'grch37' or k == 'grch38':
- alt_genomic.append(v['hgvs_genomic_description'])
+ alt_genomic.append(v['hgvs_genomic_description'].format({'max_ref_length': 0}))
gene_id = ''
if variant.stable_gene_ids:
if 'hgnc_id' in variant.stable_gene_ids:
@@ -156,16 +163,36 @@ def format_as_table(self, with_meta=True):
pass
if variant.rna_data is None:
+ if variant.hgvs_transcript_variant is not None:
+ hgvs_transcript = variant.hgvs_transcript_variant.format({'max_ref_length': 0})
+ else:
+ hgvs_transcript = None
+
+ if variant.hgvs_refseqgene_variant:
+ hgvs_refseqgene = variant.hgvs_refseqgene_variant.format({'max_ref_length': 0})
+ else:
+ hgvs_refseqgene = None
+
+ if variant.hgvs_lrg_variant:
+ hgvs_lrg = variant.hgvs_lrg_variant.format({'max_ref_length': 0})
+ else:
+ hgvs_lrg = None
+
+ if variant.hgvs_lrg_transcript_variant:
+ hgvs_lrg_transcript = variant.hgvs_lrg_transcript_variant.format({'max_ref_length': 0})
+ else:
+ hgvs_lrg_transcript = None
+
outputstrings.append([
variant.original,
'|'.join(variant.process_warnings(string_all=True)),
select_tx,
- variant.hgvs_transcript_variant,
+ hgvs_transcript,
variant.genome_context_intronic_sequence,
variant.refseqgene_context_intronic_sequence,
- variant.hgvs_refseqgene_variant,
- variant.hgvs_lrg_variant,
- variant.hgvs_lrg_transcript_variant,
+ hgvs_refseqgene,
+ hgvs_lrg,
+ hgvs_lrg_transcript,
prot,
grch37,
grch37_vcf['chr'],
@@ -286,8 +313,31 @@ def lovd_syntax_check(self, variant):
variant.lovd_corrections = lovd_corrections
-
-
+ def format_as_vrs(self):
+ """
+ Format output into a set of VRS style outputs, does normalisation due
+ to HGVS->VRS differences.
+ """
+ vrs_convert = HGVS_to_VRS()
+ vrs_output = {}
+ error_count = 0
+ for variant in self.output_list:
+ if not variant.lose_vm:
+ variant.lose_vm = self.validator.lose_vm
+ out = vrs_convert.variant_validator_output_set_to_vrs(variant)
+ key = ''
+ if 'vrs_transcript_variant' in out:
+ key = out['vrs_transcript_variant']['id']
+ elif 'vrs_intronic_genomic_variant' in out:
+ key = out['vrs_intronic_genomic_variant']['id']
+ elif 'vrs_intergenic_genomic_variant' in out:
+ key = out['vrs_intergenic_genomic_variant']['id']
+ else:
+ error_count = error_count + 1
+ key = 'error_' + str(error_count)
+ # error state no output
+ vrs_output[key] = out
+ return vrs_output
#
# Copyright (C) 2016-2026 VariantValidator Contributors
diff --git a/VariantValidator/modules/variant.py b/VariantValidator/modules/variant.py
index 5b882f25..ab44d772 100644
--- a/VariantValidator/modules/variant.py
+++ b/VariantValidator/modules/variant.py
@@ -11,22 +11,22 @@ class Variant(object):
def __init__(self, original, quibble=None, warnings=None, write=True, primary_assembly=False, order=False,
selected_assembly=False, reformat_output=False, expanded_repeat=None):
self.original = original
- if quibble is None:
+
+ # Managed variant mappings (hgvs)objects that are updated over the course of input processing
+ if quibble is None:# working version of the input variant, hgvs object after initial input processing
self.quibble = original
else:
self.quibble = quibble
- self.hgvs_formatted = None
- self.hgvs_genomic = None
- self.hgvs_coding = None
- self.post_format_conversion = None # Used for first gapped_mapping function
- self.pre_RNA_conversion = None
- self.input_parses = None # quibble as hgvs variant object
+ self.hgvs_transcript_variant = None # specifically main hgvs transcript mapping if available
+ self.hgvs_genomic = None # main hgvs genomic mapping if available
+ self.hgvs_refseqgene_variant = None # main hgvs RefSeqGene genomic mapping, used to set LRG output data
+
+ # Variant annotations
self.transcript_type = None
self.lovd_syntax_check = None
self.shorthand_vcf = None
self.lovd_messages = None
self.lovd_corrections = None
-
if warnings is None:
self.warnings = []
else:
@@ -36,10 +36,6 @@ def __init__(self, original, quibble=None, warnings=None, write=True, primary_as
self.warnings = [warnings]
self.description = '' # hgnc_gene_info variable
self.annotations = ''
- self.coding = ''
- self.coding_g = ''
- self.genomic_r = ''
- self.genomic_g = '' # should be a hgvs obj or nothing
self.protein = ''
self.write = write
self.primary_assembly = primary_assembly
@@ -68,10 +64,8 @@ def __init__(self, original, quibble=None, warnings=None, write=True, primary_as
# Required for output
self.stable_gene_ids = None
- self.hgvs_transcript_variant = None # variant.coding but edited
self.genome_context_intronic_sequence = None
self.refseqgene_context_intronic_sequence = None
- self.hgvs_refseqgene_variant = None # genomic_r but edited
self.hgvs_predicted_protein_consequence = None
self.hgvs_lrg_transcript_variant = None
self.hgvs_lrg_variant = None # Same as hgvs_refseqgene_variant but with LRG accession
@@ -242,8 +236,7 @@ def output_dict(self, test=False):
except KeyError:
pass
try:
- del self.hgvs_predicted_protein_consequence['lrg_tlr']
- del self.hgvs_predicted_protein_consequence['lrg_slr']
+ del self.hgvs_predicted_protein_consequence['lrg_prot']
except KeyError:
pass
@@ -254,22 +247,69 @@ def output_dict(self, test=False):
'gene_ids': self.stable_gene_ids,
'annotations': self.annotations,
'transcript_description': self.description,
- 'hgvs_transcript_variant': self.hgvs_transcript_variant,
+ 'hgvs_transcript_variant': None if self.hgvs_transcript_variant is None else \
+ self.hgvs_transcript_variant.format({'max_ref_length': 0}),
'rna_variant_descriptions': self.rna_data,
'genome_context_intronic_sequence': self.genome_context_intronic_sequence,
'refseqgene_context_intronic_sequence': self.refseqgene_context_intronic_sequence,
- 'hgvs_refseqgene_variant': self.hgvs_refseqgene_variant,
- 'hgvs_predicted_protein_consequence': self.hgvs_predicted_protein_consequence,
+ 'hgvs_refseqgene_variant': None if self.hgvs_refseqgene_variant is None else \
+ self.hgvs_refseqgene_variant.format({'max_ref_length': 0}),
'validation_warnings': self.process_warnings(),
'lovd_messages': self.lovd_messages,
'lovd_corrections': self.lovd_corrections,
- 'hgvs_lrg_transcript_variant': self.hgvs_lrg_transcript_variant,
- 'hgvs_lrg_variant': self.hgvs_lrg_variant,
- 'alt_genomic_loci': self.alt_genomic_loci,
- 'primary_assembly_loci': self.primary_assembly_loci,
+ 'hgvs_lrg_transcript_variant': None if self.hgvs_lrg_transcript_variant is None else \
+ self.hgvs_lrg_transcript_variant.format({'max_ref_length': 0}),
+ 'hgvs_lrg_variant': None if self.hgvs_lrg_variant is None else \
+ self.hgvs_lrg_variant.format({'max_ref_length': 0}),
'variant_exonic_positions': self.exonic_positions,
'reference_sequence_records': self.reference_sequence_records
}
+
+ primary_loci = None
+ if self.primary_assembly_loci is not None:
+ primary_loci = {}
+ for gen, dat in self.primary_assembly_loci.items():
+ primary_loci[gen] = {}
+ primary_loci[gen]['vcf'] = dat['vcf']
+ primary_loci[gen]['hgvs_genomic_description'] = \
+ dat['hgvs_genomic_description'].format({
+ 'max_ref_length': 0})
+ dict_out['primary_assembly_loci'] = primary_loci
+
+ alt_loci = None
+ if self.alt_genomic_loci is not None:
+ alt_loci = []
+ for loc in self.alt_genomic_loci:
+ rep_loc = {}
+ for gen, dat in loc.items():
+ rep_loc[gen] = {}
+ rep_loc[gen]['vcf'] = dat['vcf']
+ rep_loc[gen]['hgvs_genomic_description'] = \
+ dat['hgvs_genomic_description'].format({
+ 'max_ref_length': 0})
+ alt_loci.append(rep_loc)
+ dict_out['alt_genomic_loci'] = alt_loci
+
+ protein_dict = None
+ if self.hgvs_predicted_protein_consequence is not None:
+ if not test:
+ protein_dict = {'tlr':'','slr':'','lrg_tlr':'','lrg_slr':''}
+ else:
+ protein_dict = {'tlr':'','slr':''}
+ if 'prot' in self.hgvs_predicted_protein_consequence:
+ protein_dict['tlr'] = self.hgvs_predicted_protein_consequence['prot'].format({
+ 'max_ref_length': 0})
+ protein_dict['slr'] = self.hgvs_predicted_protein_consequence['prot'].format({
+ 'max_ref_length': 0,
+ 'p_3_letter':False})
+ if 'lrg_prot' in self.hgvs_predicted_protein_consequence:
+ protein_dict['lrg_tlr'] = self.hgvs_predicted_protein_consequence['lrg_prot'].format({
+ 'max_ref_length': 0})
+ protein_dict['lrg_slr'] =self.hgvs_predicted_protein_consequence['lrg_prot'].format({
+ 'max_ref_length': 0,
+ 'p_3_letter':False})
+ dict_out['hgvs_predicted_protein_consequence'] = protein_dict
+
return dict_out
def is_obsolete(self):
diff --git a/VariantValidator/modules/vrs_utils.py b/VariantValidator/modules/vrs_utils.py
new file mode 100644
index 00000000..48761869
--- /dev/null
+++ b/VariantValidator/modules/vrs_utils.py
@@ -0,0 +1,872 @@
+"""
+A set of functions for handling VRS style data, for now focusing on creating
+VRS output from hgvs input.
+The first function makes a complete VRS output for a given completed
+variantValidator output variant, for not converting data into "aleles"
+(hgvs variants) and Cis-Phased Blocks, protin is not yet handled (this probably
+needs Terminus) Derivative Molecule and Adjacency are not yet handled (and would
+probably be inputted and processed as hgvs for the latter, or not at all for the
+former, anyway.
+
+Each of the following are then used to produce the VRS components.
+"""
+import base64
+import hashlib
+import os
+import copy
+from configparser import ConfigParser
+from biocommons.seqrepo import SeqRepo
+from VariantValidator.modules.complex_descriptions import FEInterval
+try:
+ from VariantValidator.settings import CONFIG_DIR
+except ImportError:
+ CONFIG_DIR = None
+#from vvhgvs.location import Interval
+
+class HGVS_to_VRS:
+ """
+ A module for creating VRS output from HGVS input
+ To avoid overcomplicating the core VV object this code is kept independent
+ from VV, however it should normally only be used by VV instances. We also
+ provide facilities for setting alternative ID and seq data/length sources.
+ """
+ def __init__(self,seq_repo = False,id_fetch = False,
+ ref_fetch_blocksize = 10, cache = False):
+ """
+ Module init
+ set "seq_repo" to use a pre-existing seqrepo connection, for now this
+ is somewhat direct and is used for the id_fetch too by default, but
+ we are set up to change this in the future if needed
+ set id_fetch to use a substitute other than seqrepo as a fetch location
+ for the ID this could be the VVTA or VV database if needed, though
+ currently they only work for transcripts
+ set ref_fetch_blocksize to change the seqrepo fetch amount, given that
+ the source data is block compressed the time savings from smaller
+ than ten are probably not worth it even for data which nearly never
+ "rolls" the input seq
+ set cache to true or a dict to cache the results for this object
+ instance, (or use the existing dict as a cache). This cache is
+ intended to be used for a single user input set, which will usually
+ share lots of genomic & prot mappings. It does not currently limit
+ the result set size or anything else. If cache is set to false
+ variant_validator_output_set_to_vrs will partly overrule this to
+ create a per output-set cache to limit the re-normalisation of
+ already normalised genomic mappings.
+ """
+ if seq_repo:
+ self.seq_repo = seq_repo
+ elif CONFIG_DIR:
+ config = ConfigParser()
+ config.read(CONFIG_DIR)
+ seq_repo_path = os.path.join(config["seqrepo"]["location"],
+ config["seqrepo"]["version"])
+ self.seq_repo = SeqRepo(seq_repo_path)
+ else:
+ raise ValueError("HGVS_to_VRS conversion eitehr needs a VV "
+ "CONFIG_DIR or a already set up seq_repo")
+ if id_fetch:
+ self.id_fetch = id_fetch
+ else:
+ self.id_fetch = self.get_vrs_id_for_seq
+ self.ref_fetch_blocksize = ref_fetch_blocksize
+ if isinstance(cache,dict):
+ self.cache = cache
+ elif cache:
+ self.cache = {}
+ else:
+ self.cache = False
+
+ self.sorted_vrs_digest_keys = {
+ "SequenceLocation":["end", "sequenceReference","start","type"],
+ "Allele":["location","state","type"],
+ "LiteralSequenceExpression":['sequence', 'type'],
+ "ReferenceLengthExpression":[
+ 'length','repeatSubunitLength','type'],
+ "LengthExpression":['length','type'],
+ "SequenceReference":['refgetAccession', 'type'],
+ }
+ self.vrs_digest_prefixes = {
+ "SequenceLocation":'ga4gh:SL.',
+ "Allele":'ga4gh:VA.',
+ }
+
+ def variant_validator_output_set_to_vrs(self,variant):
+ """"
+ Convert a set of VV equivalent results into a VRS format, dataset.
+
+ VV has the following fields that may contain differing HGVS data:
+ hgvs_transcript_variant,
+ genome_context_intronic_sequence,
+ refseqgene_context_intronic_sequence,
+ hgvs_refseqgene_variant,
+ hgvs_lrg_transcript_variant,
+ hgvs_lrg_variant,
+ alt_genomic_loci,
+ primary_assembly_loci,
+ exonic_positions,
+
+ rna_data gets used instead of hgvs_transcript_variant when mapping n/a
+ hgvs_predicted_protein_consequence is used for valid transcripts
+ To avoid un-needed churn when variant==variant and ID/VRS ID==ID/VRS ID
+ we skip/reuse.
+ The current output structure is:
+ {
+ "selected_assembly': variant.selected_assembly,
+ "submitted_variant': variant.original,
+
+ "gene_ids":{}
+ "hgvs_transcript_variant":VRS (if available and not intronic)
+ "hgvs_refseqgene_variant": VRS likewise (also tagged with redundant
+ "hgvs_lrg_transcript_variant" ?)
+ "hgvs_lrg_variant": VRS#
+ "predicted prot consequence":VRS?
+ "primary_assembly_loci":{VRS set}
+ "alt_genomic_loci":[VRS list]
+ "exonic_positions": variant.exonic_positions, # if wrt transcript
+ "warnings":[warn list] # Set of VariantValidator warnings:
+ # 'lovd_messages' 'lovd_corrections'
+ # include bonus "VRSConversionWarning:" for:
+ # "intronic type variant descriptions not allowed
+ # in the VRS standard"
+ # also add "VRS variant data for ### between
+ # transcript locations so gene data has not been
+ # attached" for r type?
+ }
+
+ Add annot_level to get annotation skipped or used for partial filtering?
+ """
+ output = {
+ 'selected_assembly': variant.selected_assembly,
+ 'submitted_variant': variant.original,
+ 'warnings_and_messages': {
+ 'validation_warnings': variant.process_warnings(),
+ 'lovd_messages': variant.lovd_messages,
+ 'lovd_corrections': variant.lovd_corrections,
+ },
+ # Do we want to skip these 2 even as empty fields for intergenic
+ # transcripts?
+ "gene_ids":variant.stable_gene_ids,
+ 'transcript_description': variant.description,
+ }
+ if variant.gene_symbol:
+ if not output["gene_ids"]:
+ output["gene_ids"] = {}
+ if isinstance(output["gene_ids"], dict):
+ output["gene_ids"]['current_symbol'] = variant.gene_symbol
+ # annotations is already JSON (eg below), and relatively redundant with
+ # the other data, should add here if we want it but skip for now.
+ # e.g. annotation set: '{"db_xref": {"select": false, "ncbigene":
+ # "7840", "ensemblgene": null, "hgnc": "HGNC:428", "CCDS": null},
+ # "chromosome": "2", "map": "2p13.1", "note":
+ # "ALMS1 centrosome and basal body associated protein", "variant": "1",
+ # "refseq_select": false, "mane_select": false, "ensembl_select": false,
+ # "mane_plus_clinical": false}
+
+ # abort on bad output_type_flag, or other failure flag can be one of:
+ # 'gene' 'warning' 'mitochondrial' or 'intergenic'
+ if output['warnings_and_messages']['validation_warnings'] == [""] or \
+ variant.output_type_flag == 'warning':
+ return output
+ pos_warnings = \
+ "VRSUncertainPositionWarning:VRS description conversion of hgvs "\
+ "data with unknown locations is not fully supported, normalisation"\
+ " is disabled and other data, e.g. ins vs delins data, may be lost."
+
+ if variant.rna_data:
+ assert not variant.hgvs_transcript_variant
+ assert not variant.primary_assembly_loci
+ # :r. RNA versions are independent of :c. versions, supposed to be
+ # derived directly from RNA sequencing data, and as such skip
+ # genomic and standard cDNA variant based validation steps.
+ # We add a warning message about this. For now the default one, but
+ # fold the output into hgvs_transcript_variant.
+ # content :"usage_warnings", "rna_variant","translation" and
+ # 'translation_slr'
+ # tlr translation is skipped due to vrs code relying on single
+ # letter alphabet
+ output['vrs_transcript_variant'] = self.hgvs_single_var_to_vrs(
+ variant.rna_data['rna_variant'],variant)
+ if variant.rna_data['translation_slr'].posedit:
+ output['vrs_predicted_protein_consequence'] = \
+ self.hgvs_single_var_to_vrs(
+ variant.rna_data['translation_slr'])
+ output['warnings_and_messages']['r_type_input'] = \
+ variant.rna_data['usage_warnings']
+ if isinstance(variant.rna_data['rna_variant'], FEInterval):
+ output['warnings_and_messages']['vrs_output_warnings'] = [
+ pos_warnings]
+ return output
+
+ # now check for intergenic and/or intronic
+ reset_cache = False
+ if self.cache is False:
+ self.cache = {}
+ reset_cache = True
+ # use output flag instead?
+ if variant.hgvs_transcript_variant and not (
+ variant.hgvs_transcript_variant.type in ['c','n'] and
+ self._intronic(variant.hgvs_transcript_variant)):
+ output['vrs_transcript_variant'] = self.hgvs_single_var_to_vrs(
+ variant.hgvs_transcript_variant,variant)
+ if variant.hgvs_refseqgene_variant:
+ output['vrs_refseqgene_variant'] = self.hgvs_single_var_to_vrs(
+ variant.hgvs_refseqgene_variant,variant)
+ if variant.hgvs_predicted_protein_consequence and \
+ variant.hgvs_predicted_protein_consequence['prot'] and \
+ variant.hgvs_predicted_protein_consequence['prot'].posedit and \
+ getattr(
+ variant.hgvs_predicted_protein_consequence['prot'].posedit,
+ 'edit',False):
+ output['vrs_predicted_protein_consequence'] = \
+ self.hgvs_single_var_to_vrs(
+ variant.hgvs_predicted_protein_consequence['prot'])
+ # LRG is redundant due to VRS checksum based seq id, but do we want
+ # to add it as a alias in a comment for the output object?
+ # the RSG/LRG vrs_predicted_protein_consequence is also skipped, do
+ # we want to add this too?
+ if isinstance(variant.hgvs_transcript_variant, FEInterval):
+ output['warnings_and_messages']['vrs_output_warnings'] = [
+ pos_warnings]
+ elif variant.hgvs_transcript_variant:
+ # add warning for intronic data
+ output['warnings_and_messages']['vrs_output_warnings'] = [ (
+ "VRSIntronWarning: VRS does not handle mappings shared between"
+ " genomic and transcript reference sequences. As such only the "
+ "genomic mappings for this hgvs intronic transcript "
+ "variant are preserved.")]
+ if isinstance(variant.hgvs_transcript_variant, FEInterval):
+ output['warnings_and_messages']['vrs_output_warnings'].append(
+ pos_warnings)
+
+ if variant.hgvs_genomic:
+ output['vrs_intronic_genomic_variant'] = \
+ self.hgvs_single_var_to_vrs(variant.hgvs_genomic)
+ # RSG data
+ if variant.hgvs_refseqgene_variant:
+ output['vrs_refseqgene_variant'] = self.hgvs_single_var_to_vrs(
+ variant.hgvs_refseqgene_variant)
+ elif variant.hgvs_genomic or variant.hgvs_refseqgene_variant:
+ if variant.hgvs_genomic:
+ output['vrs_intergenic_genomic_variant'] = \
+ self.hgvs_single_var_to_vrs(variant.hgvs_genomic)
+ # RSG data
+ if variant.hgvs_refseqgene_variant:
+ output['vrs_refseqgene_variant'] = self.hgvs_single_var_to_vrs(
+ variant.hgvs_refseqgene_variant)
+ if (
+ variant.hgvs_genomic and
+ isinstance(variant.hgvs_genomic, FEInterval)) or (
+ variant.hgvs_refseqgene_variant and
+ isinstance(variant.hgvs_refseqgene_variant, FEInterval)):
+ output['warnings_and_messages']['vrs_output_warnings'] = [
+ pos_warnings]
+
+
+ # continue for universally (non :r. included data)
+ primary_assembly_loci = {}
+ if variant.primary_assembly_loci:
+ primary_assembly_loci = variant.primary_assembly_loci
+ output['VRS_mappings_for_primary_assemblies'] = {}
+ for gen in primary_assembly_loci:
+ if gen in ['hg19', 'hg38']:
+ continue
+ output['VRS_mappings_for_primary_assemblies'][gen] = \
+ self.hgvs_single_var_to_vrs(
+ primary_assembly_loci[gen][
+ 'hgvs_genomic_description'])
+ output['VRS_mappings_for_alt_genomic_loci'] = []
+ alt_genomic_loci = []
+
+ if variant.alt_genomic_loci:
+ alt_genomic_loci = variant.alt_genomic_loci
+ output['VRS_mappings_for_alt_genomic_loci'] = []
+ for loci in alt_genomic_loci:
+ gen_key = list(loci.keys())[0]
+ if gen_key in ['hg19', 'hg38']:
+ continue
+ output['VRS_mappings_for_alt_genomic_loci'].append(
+ self.hgvs_single_var_to_vrs(
+ loci[gen_key]['hgvs_genomic_description']
+ ))
+ # Crudely re-set cache, relies on us not caching this object in odd
+ # ways later, but if we do that we probably need a full caching system
+ # not something this simple.
+ if reset_cache:
+ self.cache = False
+
+ return output
+
+ def _intronic(self,hgvs):
+ if isinstance(hgvs.posedit.pos,FEInterval):
+ if (hgvs.posedit.pos.start.start.offset or
+ hgvs.posedit.pos.start.end.offset or
+ hgvs.posedit.pos.end.start.offset or
+ hgvs.posedit.pos.end.end.offset):
+ return True
+ elif (hgvs.posedit.pos.start.offset or hgvs.posedit.pos.end.offset):
+ return True
+ return False
+
+ def hgvs_single_var_to_vrs(self, hgvs_variant,variant = False):
+ "convert a hgvs with a single location to VRS, if possible"
+ # first detect intronic variants and abort for them
+ # VRS only does the genomic sequnce in this case
+ if not self.cache is False:
+ hgvs_key = str(hgvs_variant)
+ if hgvs_key in self.cache:
+ return self.cache[hgvs_key]
+ if hgvs_variant.type in ['c','n','t'] and self._intronic(hgvs_variant):
+ return None
+ if hgvs_variant.type == 'c':
+ # fix transcript coordinates to n
+ if not variant:
+ raise ValueError(
+ "Meta-variant with a variant mapper needed for c->n case")
+ if hgvs_variant.posedit.pos.uncertain:
+ ref_seq = hgvs_variant.posedit.edit.ref
+ # handle paired ambig ends
+ if isinstance(hgvs_variant.posedit.pos, FEInterval):
+ hgvs1 = copy.deepcopy(hgvs_variant)
+ hgvs1.posedit.pos = hgvs1.posedit.pos.start
+ hgvs1 = variant.lose_vm.c_to_n(hgvs1)
+ hgvs2 = copy.deepcopy(hgvs_variant)
+ hgvs2.posedit.pos = hgvs2.posedit.pos.end
+ hgvs2 = variant.lose_vm.c_to_n(hgvs2)
+ hgvs_variant.type = 'n'
+ hgvs_variant.posedit.pos.start = hgvs1.posedit.pos
+ hgvs_variant.posedit.pos.end = hgvs2.posedit.pos
+ else:
+ hgvs_variant = variant.lose_vm.c_to_n(hgvs_variant)
+ # prevent ref for unc pos from being reset (eg NNN>TTTGGGTTTGGG)
+ hgvs_variant.posedit.edit.ref = ref_seq
+ else:
+ hgvs_variant = variant.lose_vm.c_to_n(hgvs_variant)
+
+ # From this point genomic and transcript VRS should handle the same
+ # For objects IDs using child objects as variables the "canonical"
+ # set of content is used for the ID
+
+ # fetch vrs id for sequence
+ # Derive "VRS refrence" using ID e.g.
+ # {
+ # "type": "SequenceReference",
+ # "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul",
+ # "label": "NC_000007.14"
+ #}
+ #GA4GH Digest Prefix:None Inherent:[‘refgetAccession’, ‘type’]
+ vrs_ref_seq = {
+ "type": "SequenceReference",
+ "refgetAccession":self.id_fetch(hgvs_variant.ac),
+ "label": str(hgvs_variant.ac)}
+ # Handle sequences with start-stop ranges, normalisation skipped
+ if hgvs_variant.posedit.pos.uncertain:
+ # could check for FEInterval but since this sets uncertain don't
+ # for now. Currently we do not attempt to handle VRS types more
+ # complex than LiteralSequenceExpression.
+ # Ranges are specified but in VRS 2.1, but not
+ # normalisation for range input so we don't for now.
+ if not isinstance(hgvs_variant.posedit.pos, FEInterval):
+ vrs_start = [
+ int(hgvs_variant.posedit.pos.start.base)-1,
+ int(hgvs_variant.posedit.pos.end.base)]
+ vrs_end = [
+ int(hgvs_variant.posedit.pos.start.base)-1,
+ int(hgvs_variant.posedit.pos.end.base)]
+ else:
+ vrs_start = [
+ int(hgvs_variant.posedit.pos.start.start.base)-1,
+ int(hgvs_variant.posedit.pos.start.end.base)]
+ vrs_end = [
+ int(hgvs_variant.posedit.pos.end.start.base)-1,
+ int(hgvs_variant.posedit.pos.end.end.base)]
+ vrs_loc = {
+ "type": "SequenceLocation",
+ "sequenceReference":vrs_ref_seq,
+ "start":vrs_start,
+ "end":vrs_end,
+ }
+ # if del set length expr, if dupe likewise, else handle as
+ # LiteralSequenceExpression
+ if hgvs_variant.posedit.edit.type in 'dup':
+ min_len = 2* max(vrs_end[0] - vrs_start[1],0)
+ max_len = 2*(vrs_end[1] - vrs_start[0])
+ vrs_state = {
+ "type": "LengthExpression",
+ "length": [min_len,max_len]}
+ elif hgvs_variant.posedit.edit.alt:
+ # ins, sub, inv, or delins
+ vrs_state = {
+ "type": "LiteralSequenceExpression",
+ "sequence": hgvs_variant.posedit.edit.alt,
+ }
+ else:
+ # del bases represent the deleted ref length
+ # so length is length - ref length
+ min_len = max(
+ vrs_end[0] - vrs_start[1] - \
+ len(hgvs_variant.posedit.edit.ref),
+ 0)
+ max_len = vrs_end[1] - vrs_start[0] - \
+ len(hgvs_variant.posedit.edit.ref)
+ vrs_state = {
+ "type": "LengthExpression",
+ "length":[min_len,max_len]}
+ vrs_allele = {
+ "type": "Allele",
+ "location":vrs_loc,
+ "state":vrs_state,}
+ vrs_allele['id'] = self.vrs_flatten(vrs_allele,digest=True)
+ if not self.cache is False:
+ self.cache[hgvs_key] = vrs_allele
+ return vrs_allele
+ # Derive normalised start and stop
+ # Derive VRS sequence location from VRS sequence and normalised location
+ # start by converting hgvs (1 based ins special) to vrs (0 based)
+ # coordinates, also handle empty refs, this probably should not be an
+ # issue for us in normal use but still needs to be handled for the
+ # general case.
+ ref_seq = ''
+ alt_seq = ''
+ hgvs_type = hgvs_variant.posedit.edit.type
+ if hgvs_type == 'ins':
+ #may need to switch to {"type": "Number", "value": num} post VRS 2.1
+ vrs_start = int(hgvs_variant.posedit.pos.start.base)
+ vrs_end = int(hgvs_variant.posedit.pos.end.base) -1
+ ref_seq = ''
+ alt_seq = hgvs_variant.posedit.edit.alt
+ else:
+ vrs_start = int(hgvs_variant.posedit.pos.start.base) -1
+ vrs_end = int(hgvs_variant.posedit.pos.end.base)
+ ref_seq = hgvs_variant.posedit.edit.ref
+ if not ref_seq:
+ ref_seq = self.seq_repo[hgvs_variant.ac][vrs_start:vrs_end]
+ if hgvs_type == 'dup':
+ alt_seq = ref_seq + ref_seq
+ elif hgvs_type == 'identity':
+ alt_seq = ref_seq
+ else:
+ alt_seq = hgvs_variant.posedit.edit.alt
+
+ # To finish the creation of a VRS allele we now need to derive the
+ # state, which could be either a ReferenceLengthExpression or a
+ # LiteralSequenceExpression
+
+ # If both the ref and alt disappears on down-normalisation (or variant
+ # type is otherwise is equal) ReferenceLengthExpression is used with
+ # repeatSubunitLength set to the total length
+
+ # cheat shortcut for = variants
+ if ref_seq == alt_seq:
+ #{ "type": "Number", "value": 55}
+ vrs_loc = {
+ "type": "SequenceLocation",
+ "sequenceReference":vrs_ref_seq,
+ "start":vrs_start,
+ "end":vrs_end,
+ }
+ vrs_loc["id"] = self.vrs_flatten(vrs_loc,digest=True)
+ vrs_state = {
+ "type": "ReferenceLengthExpression",
+ "length": vrs_end - vrs_start,
+ "repeatSubunitLength": vrs_end - vrs_start
+ }
+ vrs_allele = {
+ "type": "Allele",
+ "location":vrs_loc,
+ "state":vrs_state,}
+ vrs_allele["id"] = self.vrs_flatten(vrs_allele,digest=True)
+ if not self.cache is False:
+ self.cache[hgvs_key] = vrs_allele
+ return vrs_allele
+ # need to pre zero base normalise start/end
+ full_ref, full_alt,min_ref,min_alt,vrs_start,vrs_end,derived = \
+ self.normalise_outwards(
+ hgvs_variant.ac,
+ ref_seq,alt_seq,
+ vrs_start,
+ vrs_end)
+ vrs_loc = {
+ "type": "SequenceLocation",
+ "sequenceReference":vrs_ref_seq,
+ "start":vrs_start,
+ "end":vrs_end,
+ }
+ vrs_loc["id"] = self.vrs_flatten(vrs_loc,digest=True)
+
+ #Do we want to "assert min_alt or min_ref"? This should only happen on
+ # == and be disallowed by the above code, so skip for now.
+ # If both ref and alt are full post down-normalisation
+ # LiteralSequenceExpression is the chosen type.
+ # if we have a indel even minimalised then this must be a
+ # LiteralSequenceExpression
+ # If we have a pure (un-ambiguous location) ins post up-normalisation
+ # LiteralSequenceExpression is the chosen output type,
+ # for the other extreme ReferenceLengthExpressions are used for del's
+ if full_alt and not full_ref:#ins not norm outwards
+ vrs_state = {
+ "type": "LiteralSequenceExpression",
+ "sequence": full_alt,
+ }
+ elif full_ref and not full_alt: # del not norm outwards
+ vrs_state = {
+ "type": "ReferenceLengthExpression",
+ "length": 0,
+ "repeatSubunitLength": vrs_end-vrs_start
+ }
+ # For other del or ins after down-normalisation, that expand to delins
+ # after up-normalisation, if the ref or alt can fully be recreated from
+ # the flank we use ReferenceLengthExpressions
+ elif derived:#full_ref and full_alt is implicit beyond this point
+ vrs_state = {
+ "type": "ReferenceLengthExpression",
+ "length": len(full_alt),
+ # one of these must be 0 length, so + works as well as an if
+ "repeatSubunitLength":len(min_ref+min_alt)
+ }
+ # otherwise we use a LiteralSequenceExpression
+ elif not derived:
+ vrs_state = {
+ "type": "LiteralSequenceExpression",
+ "sequence": full_alt,
+ }
+ vrs_allele = {
+ "type": "Allele",
+ "location":vrs_loc,
+ "state":vrs_state,}
+ vrs_allele['id'] = self.vrs_flatten(vrs_allele,digest=True)
+ if not self.cache is False:
+ self.cache[hgvs_key] = vrs_allele
+ return vrs_allele
+
+ def vrs_flatten(self, vrs_dict, dict_keys = None, digest = False):
+ """
+ Derive a canonical VRS object or it's digest ID from a dictionaried set
+ of VRS type inputs.
+ VRS demands not the contents of the fields concatenated somehow, but
+ instead a RFC 8785 Canonicalised JSON object containing just said fields
+ (with IDs), because we can't have simple things.
+ Keys must be sorted alphabetically, but we trust that any given
+ dict_keys are pre-sorted.
+ We also presume that the object type is accurate, and all numbers are
+ integers, this fits our current use case.
+ The official VRS code uses 'canoicaljson' as a dependency, but given the
+ limits on the allowed input, this is overkill.
+ """
+ if dict_keys:
+ # force specific dict keys, used in development,
+ # may be removed later
+ keys = dict_keys
+ else:
+ keys = self.sorted_vrs_digest_keys[vrs_dict['type']]
+ json = '{'
+ for key in keys:
+ if isinstance(vrs_dict[key],int):
+ json = json + f'"{key}":{vrs_dict[key]},'
+ elif isinstance(vrs_dict[key],str):
+ json = json + f'"{key}":"{vrs_dict[key]}",'
+ elif isinstance(vrs_dict[key],list):
+ assert len(vrs_dict[key]) == 2
+ # 1 must be int, other may be None, start id looks like
+ def ga4gh_digest(seq):
+ digest = hashlib.sha512(seq.encode('utf-8')).digest()
+ url_safe_di = base64.urlsafe_b64encode(digest[:24]).decode("utf-8")
+ return f"ga4gh:SQ.{url_safe_di}"
+ """
+ # we could also use VVTA for non genomic if it is faster, but this may
+ # need a different translation for the url safe encoding
+ out = self.seq_repo.translate_alias(hgvs_seq_id)
+ for seq_id in out:
+ if seq_id.startswith('ga4gh:SQ.'):
+ return seq_id[6:]
+ return False
+ #Failure to find ID for sequence should trigger a KeyError in SeqRepo
+
+ def _trim(self, ref_seq, alt_seq):
+ """
+ Trim a VRS compliant ref and alt seq to match the VRS standard
+ Trim suffix (trim right) first then prefix (left), this is specified as
+ the correct trim order in the VRS standard
+ """
+ # Early return if we can't trim (only ins or del)
+ if not ref_seq or not alt_seq:
+ return ref_seq, alt_seq, 0, 0
+ # Early return for ==, is not classically normalised
+ if alt_seq == ref_seq:
+ # return empty to flag this as ==
+ return '', '', 0, 0
+
+ loop_len = len(ref_seq)
+ # Early return shortcut for most common single base transition
+ if loop_len == 1 and len(alt_seq) == 1:
+ return ref_seq, alt_seq, 0, 0
+
+ # Finally start to trim possible, hgvs delins type
+ #Trim Right
+ loop_len = min(loop_len,len(alt_seq))
+ alt_len = 0
+ for i in range(-1,-loop_len-1,-1):
+ if ref_seq[i] == alt_seq[i]:
+ alt_len = i
+ else:
+ break
+ if alt_len:
+ ref_seq = ref_seq[:alt_len]
+ alt_seq = alt_seq[:alt_len]
+ sufix = alt_len
+
+ # Early return if fully trimmed i.e. one seq is a subset of the other
+ loop_len = min(len(ref_seq),len(alt_seq))
+ if not loop_len:
+ return ref_seq, alt_seq, 0, sufix
+
+ #Trim Left
+ alt_len = 0
+ for i in range(loop_len):
+ if ref_seq[i] == alt_seq[i]:
+ alt_len = i + 1
+ else:
+ break
+ if alt_len is not None:
+ ref_seq = ref_seq[alt_len:]
+ alt_seq = alt_seq[alt_len:]
+
+ return ref_seq, alt_seq, alt_len, sufix
+
+ def _push_r(self,target_ac,start,ref_flank,roll_seq):
+ """
+ Function for "pushing" a minimised differing sequence along a reference
+ this function acts by "rolling" the diff sequence right. Used to VRS
+ normalise sequence locations outwards to the full possible location
+ span.
+ """
+ edge_found = False
+ curr_roll_pos = 0
+ ref_chunk_start = 0
+ roll_len = len(roll_seq)
+ fully_derived = False
+ flank_section = ''
+ nomalised_edge = start
+ curr_flank_idx = 0
+ while ref_flank:
+ for curr_flank_idx, flank_char in enumerate(ref_flank):
+ if flank_char != roll_seq[curr_roll_pos]:
+ edge_found = True
+ break
+ curr_roll_pos = curr_roll_pos + 1
+ if curr_roll_pos >= roll_len:
+ fully_derived = True
+ curr_roll_pos = 0
+ if edge_found:
+ nomalised_edge = start + ref_chunk_start + curr_flank_idx
+ flank_section = flank_section + \
+ ref_flank[:curr_flank_idx]
+ break
+ ref_chunk_start = ref_chunk_start + len(ref_flank)
+ flank_section = flank_section + ref_flank
+ ref_flank = self.seq_repo[target_ac][
+ start + ref_chunk_start:
+ start + ref_chunk_start + self.ref_fetch_blocksize]
+ if fully_derived:
+ bases_non_derived = 0
+ else:
+ bases_non_derived = roll_len-curr_roll_pos
+ if not edge_found:
+ if not ref_flank:
+ curr_flank_idx = 0
+ nomalised_edge = start + ref_chunk_start + curr_flank_idx
+
+ return flank_section,nomalised_edge,bases_non_derived
+
+ def _push_l(self,target_ac,start,ref_flank,roll_seq):
+ """
+ Function for "pushing" a minimised differing sequence along a reference
+ this function acts by "rolling" the diff sequence left. Used to VRS
+ normalise sequence locations outwards to the full possible location
+ span.
+ """
+ edge_found = False
+ curr_roll_pos = 0
+ ref_chunk_start = 0
+ roll_len = len(roll_seq)
+ fully_derived = False
+ curr_flank_idx = 0
+ roll_seq = roll_seq[::-1]
+ flank_section = ''
+ nomalised_edge = 0 # if we stop before an edge is found then we hit 0
+ while ref_flank:
+ curr_flank_idx = 0
+ for curr_flank_idx, flank_char in enumerate(reversed(ref_flank)):
+ if flank_char != roll_seq[curr_roll_pos]:
+ edge_found = True
+ break
+ curr_roll_pos = curr_roll_pos + 1
+ if curr_roll_pos >= roll_len:
+ fully_derived = True
+ curr_roll_pos = 0
+ if edge_found:
+ nomalised_edge = start - ref_chunk_start - curr_flank_idx
+ if curr_flank_idx :
+ flank_section = ref_flank[-curr_flank_idx:] + flank_section
+ break
+ ref_chunk_start = ref_chunk_start + len(ref_flank)
+ flank_section = ref_flank + flank_section
+ # existing end was truncated due to sequence length
+ if start - ref_chunk_start > 0:
+ ref_flank = self.seq_repo[target_ac][
+ max(start-ref_chunk_start-self.ref_fetch_blocksize,0):
+ start - ref_chunk_start]
+ else:
+ ref_flank = ''
+ if fully_derived:
+ bases_non_derived = 0
+ else:
+ bases_non_derived = roll_len-curr_roll_pos
+ return flank_section,nomalised_edge,bases_non_derived
+
+ def normalise_outwards(
+ self,
+ target_ac,
+ ref_seq,alt_seq,
+ start,end):
+ """
+ Normalise a variant, defined in 0 based coordinates, outwards to the
+ limits of its ambiguity e.g. a ins GC in GCGC becomes ref GCGC alt
+ GCGCGC.
+
+ This is not 'gap/mapping normalisation' like what VV does inside the
+ hard left/right hgvs2vcf functions, and as such ignores things like
+ intron-exon boundaries, which are not mentioned in the VRS standard for
+ normalisation, thus if we want to handle these then they need to be
+ handled external to this normalisation step.
+
+ This should otherwise be equivalent to fully pushing right and left, for
+ ambiguous locations, at the same time.
+ """
+ if not ref_seq:
+ assert start == end
+ else:
+ assert len(ref_seq) == end - start, ref_seq+str(end)+' '+str(start)
+ if not alt_seq:
+ alt_seq = '' # for del alt seq may start as None
+ # We first minimise then re-normalise back to full, minimalisation is
+ # is needed to allow "rolling" for variants like del C ins CGC in a GC
+ # stretch. We may be able to skip this step with hgvs pre-normalise d
+ # input.
+ orig_ref = ref_seq
+ orig_alt = alt_seq
+ ref_seq, alt_seq, prefix, suffix = self._trim(ref_seq, alt_seq)
+ # VRS does not do any normalisation on == (which trim to '')
+ if not ref_seq and not alt_seq:
+ return orig_ref,orig_alt,orig_ref,orig_alt,start,end,0
+ # suffix is negative so handle a such
+ start = start + prefix
+ end = end + suffix
+ # VRS also does not push/expand normalise indels (or base transitions)
+ if ref_seq and alt_seq:
+ return ref_seq, alt_seq, ref_seq, alt_seq,\
+ start, end,0
+ # we must now either have an ins or a del but not a delins
+
+ # First grab seq
+ ref_left_flank = ''
+ ref_right_flank = ''
+ if len(ref_seq) < self.ref_fetch_blocksize*2:
+ # Even if we go past the end we only get an error if we exceed the
+ # limits of seqrepo's index, otherwise we silently truncate!
+ seq = self.seq_repo[target_ac][
+ max(start - self.ref_fetch_blocksize,0):
+ end + self.ref_fetch_blocksize]
+ if start - self.ref_fetch_blocksize >= 0:
+ ref_left_flank = seq[:self.ref_fetch_blocksize]
+ ref_right_flank = seq[self.ref_fetch_blocksize+len(ref_seq):]
+ else:
+ # this is simpler since we can run relative to the start
+ ref_left_flank = seq[:start]
+ ref_right_flank = seq[end:]
+ else:
+ ref_left_flank = self.seq_repo[target_ac][
+ max(start - self.ref_fetch_blocksize,0):start]
+ ref_right_flank = self.seq_repo[target_ac][
+ end:end + self.ref_fetch_blocksize]
+
+ # Roll in both directions, this uses a roll_length and roll_seq that is
+ # from either the seq and length of the ins, or the seq and length of
+ # the del.
+ roll_seq = ref_seq
+ if alt_seq:
+ roll_seq = alt_seq
+ ref_left_flank, start, bases_non_derived_l = self._push_l(
+ target_ac,
+ start,
+ ref_left_flank,
+ roll_seq)
+ ref_right_flank, end, bases_non_derived_r = self._push_r(
+ target_ac,
+ end,
+ ref_right_flank,
+ roll_seq)
+ fully_derived = True
+ if bases_non_derived_r + bases_non_derived_l > len(roll_seq):
+ fully_derived = False
+
+ #rf = ref_left_flank + ref_seq + ref_right_flank
+ #lf = ref_left_flank + alt_seq + ref_right_flank
+ return ref_left_flank + ref_seq + ref_right_flank,\
+ ref_left_flank + alt_seq + ref_right_flank,\
+ ref_seq, alt_seq,\
+ start, end, fully_derived
+
+#
+# Copyright (C) 2016-2026 VariantValidator Contributors
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
diff --git a/VariantValidator/modules/vvDBGet.py b/VariantValidator/modules/vvDBGet.py
index 4c974dd6..88773881 100644
--- a/VariantValidator/modules/vvDBGet.py
+++ b/VariantValidator/modules/vvDBGet.py
@@ -223,10 +223,10 @@ def get_urls(self, dict_out):
if 'NM_' in dict_out['hgvs_transcript_variant'] or 'NR_' in dict_out['hgvs_transcript_variant']:
report_urls['transcript'] = 'https://www.ncbi.nlm.nih.gov' \
'/nuccore/%s' % dict_out['hgvs_transcript_variant'].split(':')[0]
- if 'NP_' in str(dict_out['hgvs_predicted_protein_consequence']['slr']):
+ if 'NP_' in str(dict_out['hgvs_predicted_protein_consequence']['prot']):
report_urls['protein'] = 'https://www.ncbi.nlm.nih.gov' \
'/nuccore/%s' % str(
- dict_out['hgvs_predicted_protein_consequence']['slr']).split(':')[0]
+ dict_out['hgvs_predicted_protein_consequence']['prot']).split(':')[0]
if 'NG_' in dict_out['hgvs_refseqgene_variant']:
report_urls['refseqgene'] = 'https://www.ncbi.nlm.nih.gov' \
'/nuccore/%s' % dict_out['hgvs_refseqgene_variant'].split(':')[0]
@@ -247,19 +247,19 @@ def get_urls(self, dict_out):
if 'ENST' in dict_out['hgvs_transcript_variant'] and str(dict_out['selected_assembly']).lower() == 'grch37':
report_urls['transcript'] = 'https://grch37.ensembl.org/Homo_sapiens/Transcript/Summary?' \
'db=core;t=%s' % dict_out['hgvs_transcript_variant'].split(':')[0]
- if 'ENSP' in str(dict_out['hgvs_predicted_protein_consequence']['slr']) and str(dict_out['selected_assembly']).lower() == 'grch37':
+ if 'ENSP' in str(dict_out['hgvs_predicted_protein_consequence']['prot']) and str(dict_out['selected_assembly']).lower() == 'grch37':
report_urls['protein'] = 'https://grch37.ensembl.org/Homo_sapiens/Transcript/ProteinSummary?' \
'db=core;p=%s' % str(
- dict_out['hgvs_predicted_protein_consequence']['slr']).split(':')[0]
+ dict_out['hgvs_predicted_protein_consequence']['prot']).split(':')[0]
# When selected_assembly is GRCh38
if 'ENST' in dict_out['hgvs_transcript_variant'] and str(dict_out['selected_assembly']).lower() == 'grch38':
report_urls['transcript'] = 'https://www.ensembl.org/Homo_sapiens/Transcript/Summary?' \
'db=core;t=%s' % dict_out['hgvs_transcript_variant'].split(':')[0]
- if 'ENSP' in str(dict_out['hgvs_predicted_protein_consequence']['slr']) and str(dict_out['selected_assembly']).lower() == 'grch38':
+ if 'ENSP' in str(dict_out['hgvs_predicted_protein_consequence']['prot']) and str(dict_out['selected_assembly']).lower() == 'grch38':
report_urls['protein'] = 'https://www.ensembl.org/Homo_sapiens/Transcript/ProteinSummary?' \
'db=core;p=%s' % str(
- dict_out['hgvs_predicted_protein_consequence']['slr']).split(':')[0]
+ dict_out['hgvs_predicted_protein_consequence']['prot']).split(':')[0]
# "http://www.ensembl.org/id/" ? What about historic versions?????
return report_urls
diff --git a/VariantValidator/modules/vvMixinCore.py b/VariantValidator/modules/vvMixinCore.py
index 746f09be..b212fe5f 100644
--- a/VariantValidator/modules/vvMixinCore.py
+++ b/VariantValidator/modules/vvMixinCore.py
@@ -30,6 +30,7 @@
convert_seq_state_to_expanded_repeat
from VariantValidator.modules.hgvs_utils import hgvs_delins_parts_to_hgvs_obj,\
unset_hgvs_obj_ref, to_vv_hgvs
+from VariantValidator.modules.complex_descriptions import FEInterval
logger = logging.getLogger(__name__)
@@ -180,10 +181,10 @@ def validate(self,
if type(my_variant.quibble) is not str:
# already tidied input mapped to transcripts so no need to re-validate for user input type issues
- if not my_variant.hgvs_formatted:
- my_variant.hgvs_formatted = my_variant.quibble
if not my_variant.reftype:
my_variant.reftype = f':{my_variant.quibble.type}.'
+ if not my_variant.refsource:
+ my_variant.set_refsource()
if my_variant.reftype != ':g.':
toskip = self._get_transcript_info(my_variant)
if toskip:
@@ -216,6 +217,8 @@ def validate(self,
my_variant.gene_symbol = self.db.get_gene_symbol_from_transcript_id(
my_variant.quibble.ac)
try:
+ # creates mapping from quibble, also sets
+ # hgvs_transcript_variant with info from genome mapping
toskip = mappers.transcripts_to_gene(
my_variant,
self,
@@ -235,7 +238,6 @@ def validate(self,
my_variant.gene_symbol = ''
if toskip:
continue
- my_variant.hgvs_transcript_variant = my_variant.quibble
# set output to variant type specific
if my_variant.reftype in [':n.',':t.',':c.'] and my_variant.hgvs_transcript_variant != '':
@@ -403,10 +405,9 @@ def validate(self,
try:
# Test intronic variants for incorrect boundaries (see issue #169)
test_variant = copy.copy(my_variant)
- test_variant.hgvs_formatted = my_variant.quibble
- if type(test_variant.hgvs_formatted) is str:
- test_variant.hgvs_formatted = self.hp.parse_hgvs_variant(
- test_variant.hgvs_formatted)
+ if type(test_variant.quibble) is str:
+ test_variant.quibble = self.hp.parse_hgvs_variant(
+ test_variant.quibble)
# Create easy variant mapper (over variant mapper) and splign locked evm
test_variant.evm = AssemblyMapper(self.hdp,
@@ -491,8 +492,6 @@ def validate(self,
# Set some configurations
formatted_variant = my_variant.quibble
- stash_input = my_variant.quibble
- my_variant.post_format_conversion = stash_input
logger.debug("Variant input formatted, proceeding to validate.")
@@ -515,41 +514,39 @@ def validate(self,
logger.info(str(e))
- my_variant.hgvs_formatted = formatted_variant
+ my_variant.quibble = formatted_variant
- if 'LRG' in my_variant.hgvs_formatted.ac:
- my_variant.hgvs_formatted.ac.replace('T', 't')
+ if 'LRG' in my_variant.quibble.ac:
+ my_variant.quibble.ac.replace('T', 't')
else:
- my_variant.hgvs_formatted.ac = my_variant.hgvs_formatted.ac.upper()
+ my_variant.quibble.ac = my_variant.quibble.ac.upper()
- if my_variant.hgvs_formatted.type == "p" and my_variant.hgvs_formatted.posedit is None \
- and ":p.?" in str(my_variant.hgvs_formatted):
+ if my_variant.quibble.type == "p" and my_variant.quibble.posedit is None \
+ and ":p.?" in str(my_variant.quibble):
# Protein variants needed early!
toskip = format_converters.proteins(my_variant, self)
if toskip:
continue
- if hasattr(my_variant.hgvs_formatted.posedit.edit, 'alt'):
- if my_variant.hgvs_formatted.posedit.edit.alt is not None:
- my_variant.hgvs_formatted.posedit.edit.alt = \
- my_variant.hgvs_formatted.posedit.edit.alt.upper()
- if hasattr(my_variant.hgvs_formatted.posedit.edit, 'ref'):
- if my_variant.hgvs_formatted.posedit.edit.ref is not None:
- my_variant.hgvs_formatted.posedit.edit.ref = \
- my_variant.hgvs_formatted.posedit.edit.ref.upper()
-
+ if hasattr(my_variant.quibble.posedit.edit, 'alt'):
+ if my_variant.quibble.posedit.edit.alt is not None:
+ my_variant.quibble.posedit.edit.alt = \
+ my_variant.quibble.posedit.edit.alt.upper()
+ if hasattr(my_variant.quibble.posedit.edit, 'ref'):
+ if my_variant.quibble.posedit.edit.ref is not None:
+ my_variant.quibble.posedit.edit.ref = \
+ my_variant.quibble.posedit.edit.ref.upper()
try:
- formatted_variant = str(my_variant.hgvs_formatted)
+ formatted_variant = str(my_variant.quibble)
except KeyError as e:
- if "p" in my_variant.hgvs_formatted.type:
+ if "p" in my_variant.quibble.type:
error = "Invalid amino acid %s stated in description %s" % (
str(e),
str(my_variant.quibble.format({'p_3_letter':False})))
my_variant.warnings.append(error)
continue
- my_variant.set_quibble(my_variant.hgvs_formatted)
logger.debug("HVGS acceptance test passed")
# Check whether supported genome build is requested for non g. descriptions
@@ -603,12 +600,12 @@ def validate(self,
# i.e. out of bounds
# skip if we have fuzzy ends
- if isinstance(my_variant.quibble.posedit.pos.start, Interval) or \
- isinstance(my_variant.quibble.posedit.pos.end, Interval):
+ if isinstance(my_variant.quibble.posedit.pos, FEInterval) or \
+ my_variant.quibble.posedit.pos.uncertain:
continue
- if '*' in str(my_variant.hgvs_formatted.posedit):
- input_parses_copy = copy.deepcopy(my_variant.hgvs_formatted)
+ if '*' in str(my_variant.quibble.posedit):
+ input_parses_copy = copy.deepcopy(my_variant.quibble)
input_parses_copy.type = "c"
# Map to n. position
# Create easy variant mapper (over variant mapper) and splign locked evm
@@ -626,13 +623,13 @@ def validate(self,
logger.info(error)
continue
- elif my_variant.hgvs_formatted.posedit.pos.end.base < \
- my_variant.hgvs_formatted.posedit.pos.start.base:
- if my_variant.hgvs_formatted.ac not in ["NC_012920.1", "NC_001807.4"]:
+ elif my_variant.quibble.posedit.pos.end.base < \
+ my_variant.quibble.posedit.pos.start.base:
+ if my_variant.quibble.ac not in ["NC_012920.1", "NC_001807.4"]:
error = 'Interval end position ' +\
- str(my_variant.hgvs_formatted.posedit.pos.end.base) + \
+ str(my_variant.quibble.posedit.pos.end.base) + \
' < interval start position ' + \
- str(my_variant.hgvs_formatted.posedit.pos.start.base)
+ str(my_variant.quibble.posedit.pos.start.base)
my_variant.warnings.append(error)
logger.info(error)
continue
@@ -640,7 +637,7 @@ def validate(self,
# Catch missing version number in refseq/ens
is_version = re.compile(r"\d\.\d")
if ((my_variant.refsource == 'RefSeq' or my_variant.refsource == 'ENS') and
- not is_version.search(my_variant.hgvs_formatted.ac)):
+ not is_version.search(my_variant.quibble.ac)):
error = 'RefSeq variant accession numbers MUST include a version number'
my_variant.warnings.append(error)
continue
@@ -676,8 +673,7 @@ def validate(self,
continue
# RNA variants
- trapped_input = str(my_variant.hgvs_formatted)
- my_variant.pre_RNA_conversion = trapped_input
+ trapped_input = str(my_variant.quibble)
toskip = format_converters.rna(my_variant, self)
if toskip:
continue
@@ -772,16 +768,16 @@ def validate(self,
# Genomic sequence variation
# Check for gapped delins
- if (variant.genomic_g and variant.genomic_g.posedit.edit.type == 'delins' and
- variant.genomic_g.posedit.edit.alt == ""):
- logger.info(f"Delins minus an ALT sequence identified {variant.genomic_g}")
- variant.genomic_g = hgvs_delins_parts_to_hgvs_obj(
- variant.genomic_g.ac,
- variant.genomic_g.type,
- variant.genomic_g.posedit.pos,
+ if (variant.hgvs_genomic and variant.hgvs_genomic.posedit.edit.type == 'delins' and
+ variant.hgvs_genomic.posedit.edit.alt == ""):
+ logger.info(f"Delins minus an ALT sequence identified {variant.hgvs_genomic}")
+ variant.hgvs_genomic = hgvs_delins_parts_to_hgvs_obj(
+ variant.hgvs_genomic.ac,
+ variant.hgvs_genomic.type,
+ variant.hgvs_genomic.posedit.pos,
'', '')
- hgvs_genomic_variant = variant.genomic_g
+ hgvs_genomic_variant = variant.hgvs_genomic
# genomic accession
logger.debug("genomic accession")
@@ -793,10 +789,11 @@ def validate(self,
# RefSeqGene variation
logger.debug("RefSeqGene variation")
- refseqgene_variant = variant.genomic_r
+ refseqgene_variant = variant.hgvs_refseqgene_variant
if not refseqgene_variant or type(refseqgene_variant) is str and 'RefSeqGene' in refseqgene_variant:
- variant.warnings.append(refseqgene_variant)
+ if type(refseqgene_variant) is str and 'RefSeqGene' in refseqgene_variant:
+ variant.warnings.append(refseqgene_variant)
lrg_variant = ''
refseqgene_variant = ''
else:
@@ -814,13 +811,14 @@ def validate(self,
# Transcript sequence variation
logger.debug("Transcript sequence variation")
hgvs_tx_variant = None
- if variant.coding:
- if '(' in str(hgvs_tx_variant) and ')' in str(hgvs_tx_variant):
- assert False
+ # ambiguous pos variants with spans instead of start and/or stop locations can't be mapped,
+ # yet (unlike simpler ones), so we null the mapping variables for them
+ if variant.hgvs_transcript_variant and not isinstance(
+ variant.hgvs_transcript_variant.posedit.pos,FEInterval):
# transcript accession
logger.debug("transcript accession")
- hgvs_tx_variant = unset_hgvs_obj_ref(variant.coding)
+ hgvs_tx_variant = unset_hgvs_obj_ref(variant.hgvs_transcript_variant)
hgvs_transcript_variant = copy.deepcopy(hgvs_tx_variant)
transcript_accession = hgvs_transcript_variant.ac
@@ -937,6 +935,10 @@ def validate(self,
if 'NC_000' not in alt_gen_var.ac and 'NC_012920.1' not in alt_gen_var.ac and \
'NC_001807.4' not in alt_gen_var.ac:
continue
+ # skip if we have fuzzy ends in a currently non mappable form
+ if isinstance(alt_gen_var.posedit.pos.start, Interval) or \
+ isinstance(alt_gen_var.posedit.pos.end, Interval):
+ continue
try:
alt_gen_var = variant.hn.normalize(alt_gen_var)
except vvhgvs.exceptions.HGVSInvalidVariantError:
@@ -991,11 +993,14 @@ def validate(self,
# Warn not directly mapped to specified genome build
if genomic_accession:
- if primary_assembly.lower() not in list(primary_genomic_dicts.keys()):
- errors = [str(variant.hgvs_coding) + ' is not part of genome build ' + primary_assembly]
+ if genomic_accession.startswith("NG_"):
+ # Skip for NG_ which fail to find genomic mapping (done via transcript) without extra errors
+ pass
+ elif primary_assembly.lower() not in list(primary_genomic_dicts.keys()):
+ errors = [str(variant.quibble) + ' is not part of genome build ' + primary_assembly]
if self.alt_aln_method == "splign":
- errors.append(str(variant.hgvs_coding) + ' cannot be mapped directly to genome build ' + primary_assembly)
+ errors.append(str(variant.quibble) + ' cannot be mapped directly to genome build ' + primary_assembly)
errors.append('See alternative genomic loci or alternative genome builds for aligned genomic positions')
elif self.alt_aln_method == "genebuild":
@@ -1005,7 +1010,7 @@ def validate(self,
elif primary_assembly == "GRCh37" or primary_assembly == "hg19":
alt_build = "GRCh38"
# Shows the alternative genome build too
- errors.append(str(variant.hgvs_coding) + ' cannot be mapped directly to genome build ' + primary_assembly
+ errors.append(str(variant.quibble) + ' cannot be mapped directly to genome build ' + primary_assembly
+ ', did you mean ' + alt_build + '?')
variant.warnings.extend(errors)
@@ -1022,13 +1027,11 @@ def validate(self,
gene_symbol = self.db.get_gene_symbol_from_refseq_id(refseqgene_variant.ac)
variant.gene_symbol = gene_symbol
- # Add predicted protein variant dictionary this is the output form so str for final is OK
+ # Add predicted protein variant dictionary
if predicted_protein_variant != '':
predicted_protein_variant_dict = {}
- predicted_protein_variant_dict["slr"] = ''
- predicted_protein_variant_dict["tlr"] = ''
- predicted_protein_variant_dict["lrg_tlr"] = ''
- predicted_protein_variant_dict["lrg_slr"] = ''
+ predicted_protein_variant_dict["prot"] = ''
+ predicted_protein_variant_dict["lrg_prot"] = ''
if type(predicted_protein_variant) is not str:
# add protein descriptions if not N type edit
add_p_descps = True
@@ -1053,12 +1056,12 @@ def validate(self,
# convert UTR variants from p.? to p.(?)
try:
if (
- variant.hgvs_coding.posedit.pos.end.base < 0 or
- variant.hgvs_coding.posedit.pos.start.datum ==
+ variant.hgvs_transcript_variant.posedit.pos.end.base < 0 or
+ variant.hgvs_transcript_variant.posedit.pos.start.datum ==
Datum.CDS_END
):
logger.info(
- f"UTR variant {variant.hgvs_coding} identified. "
+ f"UTR variant {variant.hgvs_transcript_variant} identified. "
f"Updating from p.? to p.(=)"
)
@@ -1073,9 +1076,8 @@ def validate(self,
# Add single letter AA code to protein descriptions
- predicted_protein_variant_dict = {"tlr": str(
- predicted_protein_variant.format({'max_ref_length': 0})
- ), "slr": ''}
+ predicted_protein_variant_dict = {
+ "prot":predicted_protein_variant}
if re.search("[A-Z][a-z][a-z]1[A-Z][a-z][a-z]", str(
predicted_protein_variant.posedit)):
@@ -1100,34 +1102,22 @@ def validate(self,
posedit="({aa_1}1?)")
variant.warnings = cp_warnings
- # Set formatted tlr
- predicted_protein_variant_dict['tlr'] = \
- predicted_protein_variant.format({
- 'max_ref_length': 0})
- predicted_protein_variant_dict['slr']= \
- predicted_protein_variant.format({
- 'max_ref_length': 0,
- 'p_3_letter':False})
- # set LRG outputs
+ # Set prot for output
+ predicted_protein_variant_dict['prot'] = predicted_protein_variant
+ # set LRG output
if format_lrg is not None:
- predicted_protein_variant_dict["lrg_tlr"] = \
- format_lrg + ':' + \
- predicted_protein_variant_dict["tlr"].split(':')[1]
- predicted_protein_variant_dict["lrg_slr"] = \
- format_lrg + ':' + \
- predicted_protein_variant_dict["slr"].split(':')[1]
+ predicted_protein_variant_dict["lrg_prot"] = copy.copy(
+ predicted_protein_variant)
+ predicted_protein_variant_dict["lrg_prot"].ac = format_lrg
else:
- predicted_protein_variant_dict["lrg_tlr"] = ''
- predicted_protein_variant_dict["lrg_slr"] = ''
+ predicted_protein_variant_dict["lrg_prot"] = ''
except vvhgvs.exceptions.HGVSParseError as e:
logger.debug("Except passed, %s", e)
else:
predicted_protein_variant_dict = {}
- predicted_protein_variant_dict["slr"] = ''
- predicted_protein_variant_dict["tlr"] = ''
- predicted_protein_variant_dict["lrg_tlr"] = ''
- predicted_protein_variant_dict["lrg_slr"] = ''
+ predicted_protein_variant_dict["prot"] = ''
+ predicted_protein_variant_dict["lrg_prot"] = ''
# Add missing gene info which should be there (May have come from uncertain positions for example)
if variant.hgvs_transcript_variant and variant.gene_symbol == '':
@@ -1222,8 +1212,11 @@ def validate(self,
variant.stable_gene_ids = stable_gene_ids
variant.annotations = reference_annotations
+
+ # for now string versions of equivalent variants + rel_acc
variant.genome_context_intronic_sequence = genome_context_transcript_variant
variant.refseqgene_context_intronic_sequence = refseqgene_context_transcript_variant
+
variant.hgvs_refseqgene_variant = refseqgene_variant
variant.hgvs_predicted_protein_consequence = predicted_protein_variant_dict
variant.hgvs_lrg_transcript_variant = lrg_transcript_variant
@@ -1249,7 +1242,7 @@ def validate(self,
# Add links to reference_sequence_records
pre_out = {
'hgvs_transcript_variant':'',
- 'hgvs_predicted_protein_consequence':{'slr':''},
+ 'hgvs_predicted_protein_consequence':{'prot':''},
'hgvs_refseqgene_variant':'',
'hgvs_lrg_variant':'',
'selected_assembly':self.selected_assembly}
@@ -1259,9 +1252,10 @@ def validate(self,
pre_out['hgvs_refseqgene_variant'] = variant.hgvs_refseqgene_variant.ac
if variant.hgvs_lrg_variant:# is str
pre_out['hgvs_lrg_variant'] = variant.hgvs_lrg_variant
- if variant.hgvs_predicted_protein_consequence:
- pre_out['hgvs_predicted_protein_consequence']['slr'] = \
- variant.hgvs_predicted_protein_consequence['slr']
+ if variant.hgvs_predicted_protein_consequence and \
+ variant.hgvs_predicted_protein_consequence['prot']:
+ pre_out['hgvs_predicted_protein_consequence']['prot'] = \
+ variant.hgvs_predicted_protein_consequence['prot'].ac
ref_records = self.db.get_urls(pre_out)
if ref_records != {}:
variant.reference_sequence_records = ref_records
@@ -1375,7 +1369,8 @@ def validate(self,
variant.alt_genomic_loci = alt_genomic_loci
# Add exon numbering information see issue #
- if variant.coding != "":
+ if variant.hgvs_transcript_variant and not isinstance(
+ variant.hgvs_transcript_variant.posedit.pos,FEInterval):
try:
exs = exon_numbering.finds_exon_number(variant, self)
variant.exonic_positions = exs
@@ -1607,17 +1602,12 @@ def _apply_met_variation(data):
f"{variant.hgvs_lrg_transcript_variant.split(':c.')[0]}:c."\
+ variant.hgvs_transcript_variant.posedit.format(
{'max_ref_length': 0})
-
- # Some variant objects must be strings for back-compatibility with old output
- if variant.hgvs_transcript_variant:
- variant.hgvs_transcript_variant = \
- variant.hgvs_transcript_variant.format({'max_ref_length': 0})
- else:
+ # Some variant objects must be set on successful completion for back-compatibility
+ # with old output (we currently assert that the object starts as None in tests but
+ # also assert that it has ''/{} as output, VF needs this for primary_assembly_loci)
+ if not variant.hgvs_transcript_variant:
variant.hgvs_transcript_variant = ''
- if variant.hgvs_refseqgene_variant:
- variant.hgvs_refseqgene_variant = \
- variant.hgvs_refseqgene_variant.format({'max_ref_length': 0})
- else:
+ if not variant.hgvs_refseqgene_variant:
variant.hgvs_refseqgene_variant = ''
hgd = "hgvs_genomic_description"
def _vcf_abrv(hgvs,vcf,max_non_abrv_len=500):
@@ -1643,19 +1633,18 @@ def _vcf_abrv(hgvs,vcf,max_non_abrv_len=500):
vcf["alt"] = hgvs.posedit.edit.type.upper()
return vcf
+ if not variant.primary_assembly_loci:
+ variant.primary_assembly_loci = {}
for gen in variant.primary_assembly_loci.keys():
variant.primary_assembly_loci[gen]['vcf'] = _vcf_abrv(
variant.primary_assembly_loci[gen][hgd],
variant.primary_assembly_loci[gen]['vcf'])
- variant.primary_assembly_loci[gen][hgd] = \
- variant.primary_assembly_loci[gen][hgd].format({'max_ref_length': 0})
for loc in variant.alt_genomic_loci:
for gen in loc.keys():
loc[gen]['vcf'] = _vcf_abrv(
loc[gen][hgd],
loc[gen]['vcf'])
- loc[gen][hgd] = loc[gen][hgd].format({'max_ref_length': 0})
# Append to a list for return
batch_out.append(variant)
@@ -1777,7 +1766,7 @@ def _get_transcript_info(self, variant):
Should only be called during the validator process
"""
logger.debug("Looking for transcript info")
- hgvs_vt = variant.hgvs_formatted
+ hgvs_vt = variant.quibble
try:
self.hdp.get_tx_identity_info(str(hgvs_vt.ac))
except vvhgvs.exceptions.HGVSError as e:
@@ -1793,7 +1782,7 @@ def _get_transcript_info(self, variant):
if self.alt_aln_method != 'genebuild':
# Gene description - requires GenBank search to get all the required info, i.e. transcript variant ID
# accession number
- hgvs_object = variant.hgvs_formatted
+ hgvs_object = variant.quibble
accession = hgvs_object.ac
# Look for the accession in our database
# Connect to database and send request
@@ -1874,7 +1863,7 @@ def _get_transcript_info(self, variant):
# Ensembl databases
else:
# accession number
- hgvs_object = variant.hgvs_formatted
+ hgvs_object = variant.quibble
accession = hgvs_object.ac
# Look for the accession in our database
# Connect to database and send request
diff --git a/tests/test_variant.py b/tests/test_variant.py
index 09be1379..673f7cc7 100644
--- a/tests/test_variant.py
+++ b/tests/test_variant.py
@@ -64,18 +64,9 @@ def test_order_not_set(self):
def test_all_defaults(self):
var = Variant('NM_015120.4:c.34=')
- self.assertEqual(var.hgvs_formatted, None)
self.assertEqual(var.hgvs_genomic, None)
- self.assertEqual(var.hgvs_coding, None)
- self.assertEqual(var.post_format_conversion, None)
- self.assertEqual(var.pre_RNA_conversion, None)
- self.assertEqual(var.input_parses, None)
self.assertEqual(var.description, '')
- self.assertEqual(var.coding, '')
- self.assertEqual(var.coding_g, '')
- self.assertEqual(var.genomic_r, '')
- self.assertEqual(var.genomic_g, '')
self.assertEqual(var.protein, '')
self.assertEqual(var.output_type_flag, 'warning')
self.assertEqual(var.gene_symbol, '')
diff --git a/tests/test_vrs.py b/tests/test_vrs.py
new file mode 100644
index 00000000..34fd9abd
--- /dev/null
+++ b/tests/test_vrs.py
@@ -0,0 +1,3393 @@
+"""
+This test set tests on HGVS to VRS output conversion module to check first the
+internal functions and then the overall outputs.
+
+The extra test data, particularly test checksums, is taken from the known good
+output given in the VRS documentation.
+"""
+import copy
+import os
+import unittest
+import json
+from configparser import ConfigParser
+
+import vvhgvs
+from vvhgvs.location import Interval
+from biocommons.seqrepo import SeqRepo
+
+from VariantValidator.modules.vrs_utils import HGVS_to_VRS
+from VariantValidator.modules.complex_descriptions import FEInterval
+from VariantValidator import Validator
+from VariantValidator.modules.hgvs_utils import hgvs_obj_from_existing_edit,\
+ _hgvs_offset_pos_from_str_in
+from VariantValidator.settings import CONFIG_DIR
+
+vv = Validator()
+vv.alt_aln_method = "splign"
+
+
+class MockVVData():
+ "Mock class should be == to VV wrt to VRS output production"
+ def __init__(
+ self,
+ selected_assembly = 'GRCh38', original = 'TestOrigVarText',
+ warnings = None, lovd_messages = None, lovd_corrections = None,
+ stable_gene_ids = None, description = '', gene_symbol = '',
+ output_type_flag = None, rna_data = None,
+ hgvs_transcript_variant = '', primary_assembly_loci = None,
+ alt_genomic_loci = None, hgvs_predicted_protein_consequence = None,
+ hgvs_refseqgene_variant = None, hgvs_genomic = None):
+ self.selected_assembly = selected_assembly
+ self.original = original # submitted_variant
+ self.warnings = []
+ if self.warnings:
+ self.warnings = warnings
+ self.lovd_messages = []
+ if lovd_messages:
+ self.lovd_messages = lovd_messages
+ self.lovd_corrections = []
+ if lovd_corrections:
+ self.lovd_corrections = lovd_corrections
+ self.hgvs_genomic = hgvs_genomic
+
+ ## should be set to null if intergenic
+ self.stable_gene_ids = []
+ if stable_gene_ids:
+ self.stable_gene_ids = stable_gene_ids
+
+ self.description = description # transcript_description
+ self.gene_symbol = gene_symbol
+
+ # one of 'gene' 'warning' 'mitochondrial' or 'intergenic'
+ self.output_type_flag = output_type_flag
+
+
+ # now set individual hgvs datasets
+
+ # rna_data should be set to null if not r type variant, or else a dict
+ # containing: 'rna_variant', 'translation_slr', and 'usage_warnings'
+ self.rna_data = rna_data
+
+ # non r type transcript data
+ self.hgvs_transcript_variant = hgvs_transcript_variant
+
+ # mappings for transcript variant or main genomic variant given
+ self.primary_assembly_loci = primary_assembly_loci
+ self.alt_genomic_loci = alt_genomic_loci
+
+ # prot variation
+ self.hgvs_predicted_protein_consequence = \
+ hgvs_predicted_protein_consequence
+ # RSG variation
+ self.hgvs_refseqgene_variant = hgvs_refseqgene_variant
+ # VM / data provider
+ self.hdp = vvhgvs.dataproviders.uta.connect(pooling=True)
+ self.lose_vm = vvhgvs.variantmapper.VariantMapper(
+ self.hdp,
+ replace_reference=True,
+ prevalidation_level=None
+ ) # Variant mapper
+ def process_warnings(self):
+ return self.warnings
+
+class TestVRSOutputTrim(unittest.TestCase):
+ """
+ Tests for the internal vrs_util.py module, particularly the HGVS_to_VRS
+ object's trimming function which reduces a variant to its most minimal
+ sequence length of differing bases.
+
+ Tests are for:
+ minimise totally,
+ no change,
+ minimise from partial,
+ minimise from whole region,
+ minimise from left, and
+ minimise from right
+
+ """
+ @classmethod
+ def setup_class(self):
+ self.vrs = HGVS_to_VRS()
+
+ def test_vrs_trim_total(self):
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('AACAA','AACAA')
+ assert ref_seq == ''
+ assert alt_seq == ''
+ assert not prefix_bases_removed and not sufix_bases_removed
+
+ def test_vrs_trim_no_change(self):
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('ACGT','CTG')
+ assert ref_seq == 'ACGT'
+ assert alt_seq == 'CTG'
+ assert not prefix_bases_removed and not sufix_bases_removed
+
+ def test_vrs_trim_both_edge_partial(self):
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('AAAA','ACA')
+ assert ref_seq == 'AA'
+ assert alt_seq == 'C'
+ assert prefix_bases_removed == 1
+ assert sufix_bases_removed == -1
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('ACA','AAAA')
+ assert alt_seq == 'AA'
+ assert ref_seq == 'C'
+ assert prefix_bases_removed == 1
+ assert sufix_bases_removed == -1
+
+ def test_vrs_trim_both_edge_total(self):
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('AAAA','AACAA')
+ assert ref_seq == ''
+ assert alt_seq == 'C'
+ assert prefix_bases_removed == 2
+ assert sufix_bases_removed == -2
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('AACAA','AAAA')
+ assert alt_seq == ''
+ assert ref_seq == 'C'
+ assert prefix_bases_removed == 2
+ assert sufix_bases_removed == -2
+
+ def test_vrs_trim_from_left(self):
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('AAAA','CAA')
+ assert ref_seq == 'AA'
+ assert alt_seq == 'C'
+ assert prefix_bases_removed == 0
+ assert sufix_bases_removed == -2
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('CAA','AAAA')
+ assert ref_seq == 'C'
+ assert alt_seq == 'AA'
+ assert prefix_bases_removed == 0
+ assert sufix_bases_removed == -2
+
+ def test_vrs_trim_from_right(self):
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('AAAA','AAC')
+ assert ref_seq == 'AA'
+ assert alt_seq == 'C'
+ assert prefix_bases_removed == 2
+ assert sufix_bases_removed == 0
+ ref_seq, alt_seq, prefix_bases_removed, sufix_bases_removed = \
+ self.vrs._trim('AAC','AAAA')
+ assert ref_seq == 'C'
+ assert alt_seq == 'AA'
+ assert prefix_bases_removed == 2
+ assert sufix_bases_removed == 0
+
+class TestVRSOutputPush(unittest.TestCase):
+ """
+ Tests for the internal vrs_util.py modules push left & right used in
+ normalisation of VRS input, the testes are:
+ normal push,
+ push already done,
+ push from full opposed end,
+ push with flank amount less than repeat unit,
+ push where flank fetch amount == repeat unit length,
+ push with flank fetch amount large enough to overlap transcript end, and
+ push with fetch end == end coordinate
+ known quirks:
+ ref_left_flank/ref_right_flank input needs to be set to something (or
+ we return as if we hit the end), for now, due to loop logic
+ push does not care (or know) about the difference between del an ins
+ push function data/key
+ returns: ref_(left/right)_flank, start, bases_non_derived
+ ref_(left/right)_flank, bases to add to the flank after roll
+ start, new start location
+ bases_non_derived are the no. of bases in the roll seq not in flank
+ input: target_ac, start,ref_flank, roll_seq
+ roll_seq is the minimised ins or del in question,
+ ref_(left/right)_flank is the current left/right flank,
+ roll_seq minimised (i.e. trimmed) version of input variant
+ """
+ @classmethod
+ def setup_class(self):
+ self.vrs = HGVS_to_VRS()
+ self.longer_vrs = copy.copy(self.vrs)
+ self.longer_vrs.ref_fetch_blocksize = 10
+ self.mid_vrs = copy.copy(self.vrs)
+ self.mid_vrs.ref_fetch_blocksize = 5
+ self.short_vrs = copy.copy(self.vrs)
+ self.short_vrs.ref_fetch_blocksize = 2
+ self.ex_short_vrs = copy.copy(self.vrs)
+ self.ex_short_vrs.ref_fetch_blocksize = 1
+ # push left data:
+ # NM_003073.5
+ # cds start 204, seq AGAAGAAGA start 1288:1297
+ # flank 'TGAGATGG' 'AGAAGAAGA' 'TCCGCGAC'
+ # this has a hanging part repeat of either CA or GC depending VRS
+ # normalisation should get the whole +partial when hgvs does not
+ # NM_002111.8
+ # cds start 145 seq:
+ # CA GCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA
+ # start 198:261 (or rather 196:261)
+ # with flank is:
+ # CAAGTCCTTC
+ # CA GCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA
+ # ACAGCCGCCA
+ # longer mid seq (not used atm)
+ # NM_001350922.2:c.240+14TGGG[2] NC_000010.10:g.128358789_128358796=
+ # start seq
+ # NM_001330096.1
+ # rep CT stop 26
+ # seq CTCTCTCTCTCTCTCTCTCTCTCTCT TTCTTTCTTTCCGATGGGGAAGAGAGGGT
+ # NM_001330112.1
+ # rep GGGGCGGGTC stop 28 (or 20 for complete segments)
+ # seq (with flank) GGGGCGGGTCGGGGCGGGTC GGGGCGGG CCCACGGGCGGCCGGATTTG
+ # end seq
+ # NM_052928.3
+ # rep GTTTT start 4375 len 4405
+ # seq (w flank):
+ # ATAACACATATGCCTCCTTCTGAGTTGTTG GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT
+ # NM_002129.4
+ # rep AT start 1408 len 1441
+ # seq ATTAGCTAAATTGTTCCTCAGGTGTGTG T ATATATATATACATATATATATATATATATAT
+ def test_vrs_push_left_from_mid(self):
+ # push left from middle
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NR_110010.2',27,'C','GC')
+ assert start == 25
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GC'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NM_003073.5',1291,'AGA','AGA')
+ assert start == 1288
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'AGA'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NM_002111.8',202,'AG','CAG')
+ assert start == 196
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'CAGCAG'
+
+ def test_vrs_push_left_already_done(self):
+ # push left already done (from far push base)
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NR_110010.2',25,'A','GC')
+ assert start == 25
+ assert bases_non_derived == 2
+ assert ref_left_flank == ''
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NM_003073.5',1288,'GG','AGA')
+ assert start == 1288
+ assert bases_non_derived == 3
+ assert ref_left_flank == ''
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NM_002111.8',196,'CAAGTCCTTC','CAG')
+ assert start == 196
+ assert bases_non_derived == 3
+ assert ref_left_flank == ''
+
+ def test_vrs_push_left_from_full_right(self):
+ # push left from full right
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NR_110010.2',32,'G','CG')
+ assert start == 25
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCGCG'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NM_003073.5',1297,'A','AGA')
+ assert start == 1288
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'AGAAGAAGA'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NM_002111.8',261,'CA','GCA')
+ assert start == 196
+ assert bases_non_derived == 0
+ assert ref_left_flank == \
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA'
+
+ def test_vrs_push_left_from_full_right_w_fetch(self):
+ # push left from full right (pre fetched flank less than needed, to force internal fetch)
+ ref_left_flank, start, bases_non_derived = \
+ self.longer_vrs._push_l('NR_110010.2',32,'G','CG')
+ assert start == 25
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCGCG'
+ ref_left_flank, start, bases_non_derived = \
+ self.longer_vrs._push_l('NM_003073.5',1297,'AGA','AGA')
+ assert start == 1288
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'AGAAGAAGA'
+
+ def test_vrs_push_left_fetch_lt_repeat_size(self):
+ # push left given flank to fetch amount less than the size repeat unit (roll_seq)
+ ref_left_flank, start, bases_non_derived = \
+ self.ex_short_vrs._push_l('NR_110010.2',32,'G','CG')
+ assert start == 25
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCGCG'
+ ref_left_flank, start, bases_non_derived = \
+ self.short_vrs._push_l('NM_003073.5',1297,'GA','AGA')
+ assert start == 1288
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'AGAAGAAGA'
+ ref_left_flank, start, bases_non_derived = \
+ self.short_vrs._push_l('NM_001330112.1',10,'GTC', 'GGGGCGGGTC')
+ assert start == 0
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GGGGCGGGTC'
+
+ def test_vrs_push_left_fetch_eq_repeat_size(self):
+ # push left, flank to fetch amount == repeat unit length (roll_seq)
+ ref_left_flank, start, bases_non_derived = \
+ self.short_vrs._push_l('NR_110010.2',32,'CG','CG')
+ assert start == 25
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCGCG'
+
+ def test_vrs_push_left_partial_roll(self):
+ # test partial roll
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NR_110010.2',27,'C','GCTGC')
+ assert start == 25
+ assert bases_non_derived == 3
+ assert ref_left_flank == 'GC'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NM_003073.5',1291,'AGA','AGACAGA')
+ assert start == 1288
+ assert bases_non_derived == 4
+ assert ref_left_flank == 'AGA'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_l('NM_002111.8',199,'CAG','CAGTCAG')
+ assert start == 196
+ assert bases_non_derived == 4
+ assert ref_left_flank == 'CAG'
+
+ def test_vrs_push_left_hits_seq_start(self):
+ # test hits start
+ # NM_001330096.1
+ # rep CT stop 26
+ # seq CTCTCTCTCTCTCTCTCTCTCTCTCT TTCTTTCTTTCCGATGGGGAAGAGAGGGT
+ # NM_001330112.1
+ # rep GGGGCGGGTC stop 28 (or 20 for complete segments)
+ # seq (with flank) GGGGCGGGTCGGGGCGGGTC GGGGCGGG CCCACGGGCGGCCGGATTTG
+ # test corner case roll fetch hits 0 and rolls to 0
+ ref_left_flank, start, bases_non_derived = \
+ self.short_vrs._push_l('NM_001330096.1',4,'CT','CT')
+ assert start == 0
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'CTCT'
+ ref_left_flank, start, bases_non_derived = self.longer_vrs._push_l(
+ 'NM_001330112.1',20,'GGGGCGGGTC','GGGGCGGGTC')
+ assert start == 0
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GGGGCGGGTCGGGGCGGGTC'
+ # test corner cases: roll fetch crosses 0 (truncated final fetch)
+ # and rolls to 0
+ ref_left_flank, start, bases_non_derived = \
+ self.longer_vrs._push_l('NM_001330096.1',4,'CT','CT')
+ assert start == 0
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'CTCT'
+ ref_left_flank, start, bases_non_derived = self.longer_vrs._push_l(
+ 'NM_001330112.1',20,'GGGTCGGGGCGGGTC','GGGGCGGGTC')
+ assert start == 0
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GGGGCGGGTCGGGGCGGGTC'
+
+ # Push right data
+ # end seq
+ # NM_052928.3
+ # rep GTTTT start 4375 len 4405
+ # seq (w flank):
+ # ATAACACATATGCCTCCTTCTGAGTTGTTG GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT
+ # NM_002129.4
+ # rep AT start 1408 len 1441
+ # seq ATTAGCTAAATTGTTCCTCAGGTGTGTG T ATATATATATACATATATATATATATATATAT
+ # NR_110010.2
+ # seq start 25:32
+ # seq w flank ATGGTGGCGA GCGCGCG AGTGCAGAAG
+ # NM_003073.5
+ # cds start 204 start 1288:1297
+ # seq w flank TGAGATGG AGAAGAAGA TCCGCGAC
+ # this (NM_003073.5 rep) has a hanging partial repeat, either CA or GC
+ # depending, VRS normalisation should get whole +partial, hgvs does not
+ # NM_002111.8
+ # cds start 145 start 198:261 or rather 196:261
+ # seq with flank:
+ # CAAGTCCTTC
+ # CA GCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA
+ # ACAGCCGCCA
+ # longer mid seq (not used atm)
+ # NM_001350922.2:c.240+14TGGG[2] NC_000010.10:g.128358789_128358796=
+ # start seq
+ # NM_001330096.1
+ # rep CT stop 26
+ # seq CTCTCTCTCTCTCTCTCTCTCTCTCT TTCTTTCTTTCCGATGGGGAAGAGAGGGT
+ # NM_001330112.1
+ # rep GGGGCGGGTC stop 28 (or 20 for complete segments)
+ # seq (with flank) GGGGCGGGTCGGGGCGGGTC GGGGCGGG CCCACGGGCGGCCGGATTTG
+ # end seq
+ # NM_052928.3
+ # rep GTTTT start 4375 len 4405
+ # seq (w flank):
+ # ATAACACATATGCCTCCTTCTGAGTTGTTG GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT
+ # NM_002129.4
+ # rep AT start 1408 len 1441
+ # seq ATTAGCTAAATTGTTCCTCAGGTGTGTG T ATATATATATAC ATATATATATATATATATAT
+
+ def test_vrs_push_right_from_middle(self):
+ # push right from middle
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NR_110010.2',27,'G','GC')
+ assert start == 32
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCG'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NM_003073.5',1291,'AGA','AGA')
+ assert start == 1297
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'AGAAGA'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NM_002111.8',202,'CA','CAG')
+ assert start == 261
+ assert bases_non_derived == 0
+ assert ref_left_flank == \
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA'
+
+ def test_vrs_push_right_already_done(self):
+ # push right already done (from far push base)
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NR_110010.2',32,'A','GC')
+ assert start == 32
+ assert bases_non_derived == 2
+ assert ref_left_flank == ''
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NM_003073.5',1297,'TCCGCGAC','AGA')
+ assert start == 1297
+ assert bases_non_derived == 3
+ assert ref_left_flank == ''
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NM_002111.8',261,'ACAGCCGCCA','CAG')
+ assert start == 261
+ assert bases_non_derived == 3
+ assert ref_left_flank == ''
+
+ def test_vrs_push_right_from_full_left(self):
+ # push right from full left
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NR_110010.2',25,'G','GC')
+ assert start == 32
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCGCG'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NM_003073.5',1288,'A','AGA')
+ assert start == 1297
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'AGAAGAAGA'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NM_002111.8',196,'CA','CAG')
+ assert start == 261
+ assert bases_non_derived == 0
+ assert ref_left_flank == \
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA'
+
+ def test_vrs_push_right_from_full_left_w_forced_fetch(self):
+ # push right from full left (pre fetched flank less than needed, to force internal fetch)
+ ref_left_flank, start, bases_non_derived = \
+ self.longer_vrs._push_r('NR_110010.2',25,'G','GC')
+ assert start == 32
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCGCG'
+ ref_left_flank, start, bases_non_derived = \
+ self.longer_vrs._push_r('NM_003073.5',1288,'AGA','AGA')
+ assert start == 1297
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'AGAAGAAGA'
+
+ def test_vrs_push_right_fetch_lt_repeat_unit(self):
+ # push right given flank to fetch amount less than the size repeat unit (roll_seq)
+ ref_left_flank, start, bases_non_derived = \
+ self.ex_short_vrs._push_r('NR_110010.2',25,'G','GC')
+ assert start == 32
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCGCG'
+ ref_left_flank, start, bases_non_derived = \
+ self.short_vrs._push_r('NM_003073.5',1288,'AG','AGA')
+ assert start == 1297
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'AGAAGAAGA'
+ ref_left_flank, start, bases_non_derived = \
+ self.short_vrs._push_r('NM_001330112.1',0,'GG', 'GGGGCGGGTC')
+ assert start == 28
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GGGGCGGGTCGGGGCGGGTCGGGGCGGG'
+
+ def test_vrs_push_right_fetch_eq_repeat_unit(self):
+ # push right, flank to fetch amount == repeat unit length (roll_seq)
+ ref_left_flank, start, bases_non_derived = \
+ self.short_vrs._push_r('NR_110010.2',25,'GC','GC')
+ assert start == 32
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GCGCGCG'
+
+ def test_vrs_push_right_partial_roll(self):
+ # test partial roll
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NR_110010.2',27,'G','GCTGC')
+ assert start == 29
+ assert bases_non_derived == 3
+ assert ref_left_flank == 'GC'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NM_003073.5',1288,'AGA','AGACAGA')
+ assert start == 1291
+ assert bases_non_derived == 4
+ assert ref_left_flank == 'AGA'
+ ref_left_flank, start, bases_non_derived = \
+ self.vrs._push_r('NM_002111.8',196,'CAG','CAGTCAG')
+ assert start == 199
+ assert bases_non_derived == 4
+ assert ref_left_flank == 'CAG'
+
+ def test_vrs_push_right_hits_end(self):
+ # test hits end
+ # NM_052928.3
+ # rep GTTTT start 4375 len 4405
+ # seq (w flank):
+ # ATAACACATATGCCTCCTTCTGAGTTGTTG GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT
+ # NM_002129.4
+ # rep AT start 1408 len 1441
+ # seq ATTAGCTAAATTGTTCCTCAGGTGTGTG T ATATATATATAC ATATATATATATATATATAT'
+ # test corner case roll fetch hits last base and rolls to last base
+ ref_left_flank, start, bases_non_derived = \
+ self.mid_vrs._push_r('NM_052928.3',4375,'GTT','GTTTT')
+ assert start == 4405
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT'
+ ref_left_flank, start, bases_non_derived = \
+ self.short_vrs._push_r('NM_002129.4',1421,'AT','AT')
+ assert start == 1441
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'ATATATATATATATATATAT'
+ def test_vrs_push_right_hits_end_handle_last_fetch_gt_final_base(self):
+ # test corner case roll fetch crosses last base (truncated final fetch)
+ # and rolls to last base
+ ref_left_flank, start, bases_non_derived = \
+ self.longer_vrs._push_r('NM_052928.3',4375,'GTT','GTTTT')
+ assert start == 4405
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT'
+ ref_left_flank, start, bases_non_derived = \
+ self.longer_vrs._push_r('NM_002129.4',1421,'AT','AT')
+ assert start == 1441
+ assert bases_non_derived == 0
+ assert ref_left_flank == 'ATATATATATATATATATAT'
+
+class TestVRSOutputNorm(unittest.TestCase):
+ """
+ Tests for the internal vrs_util.py module's HGVS_to_VRS object's
+ normalise_outwards function, which should minimise and then push outwards
+ (in both directions) test structure as for the push left/right above.
+ known quirks:
+ Because the first step is minimise, followed by rolling the seq in both
+ directions the actual minimised seq can differ in roll position
+ e.g. CG vs GC. But otherwise for the same underlying variant the
+ results should always be the same,
+ push function data/key:
+ returns: full_ref, full_alt,min_ref,min_alt,vrs_start,vrs_end,derived
+ full_ref: normalised (maximised) version of the ref
+ full_alt: normalised (maximised) version of the alt
+ min_ref & min_alt: minimised version of the same, (used along with
+ derived_state for variant type detection)
+ derived_state: True/False, is the variant derived from the
+ immediate flank (eg cnv/tandem repeat)
+ input: ref_ac, ref_seq,alt_seq, vrs_start, vrs_end
+ ref_ac is the accession that the change is relevant for
+ ref_seq is the original sequence of the region
+ alt_seq is the sequence of the variant in question
+ vrs_start & vrs_end are the 0 based VRS type coordinates of the ref
+ location
+ tests done:
+ Done for both del and ins:
+ expand left, expand right, expand both directions, expand already done,
+ expand flank fetch amount less than repeat unit, expand flank amount
+ same as repeat unit, expand flank fetch amount large enough to overlap
+ transcript start/end, expand flank amount == start/end coordinate from
+ current
+
+ Also test early abort on == data.
+ """
+ @classmethod
+ def setup_class(self):
+ self.vrs = HGVS_to_VRS()
+ self.ex_short_vrs = copy.copy(self.vrs)
+ self.ex_short_vrs.ref_fetch_blocksize = 1
+ # NR_110010.2
+ # seq start 25:32
+ # seq with flank ATGGTGGCGA GCGCGCG AGTGCAGAAG
+ # NM_003073.5
+ # cds start 204 start 1288:1297
+ # seq with flank TGAGATGG AGAAGAAGA TCCGCGAC
+ # this has a hanging part repeat of either CA or GC depending
+ # VRS normalisation should get the whole +partial when hgvs does not
+ # NM_002111.8
+ # cds start 145 start 198:261 or rather 196:261
+ # seq with flank:
+ # CAAGTCCTTC
+ # CA GCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA
+ # ACAGCCGCCA
+ # longer mid seq (not used atm)
+ # NM_001350922.2:c.240+14TGGG[2] NC_000010.10:g.128358789_128358796=
+ # start seq
+ # NM_001330096.1
+ # rep CT stop 26
+ # seq CTCTCTCTCTCTCTCTCTCTCTCTCT TTCTTTCTTTCCGATGGGGAAGAGAGGGT
+ # NM_001330112.1
+ # rep GGGGCGGGTC stop 28 (or 20 for complete segments)
+ # seq (with flank) GGGGCGGGTCGGGGCGGGTC GGGGCGGG CCCACGGGCGGCCGGATTTG
+ # end seq
+ # NM_052928.3
+ # rep GTTTT start 4375 len 4405
+ # seq (with flank):
+ # ATAACACATATGCCTCCTTCTGAGTTGTTG GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT
+ # NM_002129.4
+ # rep AT start 1408 len 1441
+ # seq ATTAGCTAAATTGTTCCTCAGGTGTGTG T ATATATATATACATATATATATATATATATAT
+
+ # result set key:
+ # full_ref, full_alt, min_ref, min_alt, vrs_start, vrs_end, derived
+ self.NR_110010_2_ins_result_set = (
+ 'GCGCGCG','GCGCGCGCG','','CG',25,32,True)
+ self.NR_110010_2_ins_result_set_end = (
+ 'GCGCGCG','GCGCGCGCG','','GC',25,32,True)
+ self.NM_003073_5_ins_result_set = (
+ 'AGAAGAAGA','AGAAGAAGAAGA','','AGA',1288,1297,True)
+ self.NM_002111_8_ins_result_set = (
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ '','CAG',196,261,True)
+ self.NM_002111_8_ins_result_set_end = (
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ '','GCA',196,261,True)
+ # start seq
+ # NM_001330096.1
+ # rep CT stop 26
+ # seq CTCTCTCTCTCTCTCTCTCTCTCTCT TTCTTTCTTTCCGATGGGGAAGAGAGGGT
+ # NM_001330112.1
+ # rep GGGGCGGGTC stop 28 (or 20 for complete segments)
+ # seq (with flank) GGGGCGGGTCGGGGCGGGTC GGGGCGGG CCCACGGGCGGCCGGATTTG
+ # end seq
+ # NM_052928.3
+ # rep GTTTT start 4375 len 4405
+ # seq (w flank):
+ # ATAACACATATGCCTCCTTCTGAGTTGTTG GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT
+ # NM_002129.4
+ # rep AT start 1408 len 1441
+ # seq ATTAGCTAAATTGTTCCTCAGGTGTGTG T ATATATATATACATATATATATATATATATAT
+ self.res_set_start_NM_001330096_1 = (
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCT',
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCTCT',
+ '','TC',0,26,True)
+ self.res_set_start_NM_001330096_1_end = (
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCT',
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCTCT',
+ '','CT',0,26,True)
+ self.res_set_start_NM_001330112_1 = (
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ '','GGGGCGGGTC',0,28,True)
+ self.res_set_start_NM_001330112_1_end = (
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ '','TCGGGGCGGG',0,28,True)
+
+ self.res_set_end_NM_052928_3 = (
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ '','GTTTT',4375,4405,True)
+ self.res_set_end_NM_052928_3_end = (
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ '','TTGTT' ,4375,4405,True)
+ self.res_set_end_NM_002129_4 = (
+ 'ATATATATATATATATATAT',
+ 'ATATATATATATATATATATAT',
+ '','AT',1421,1441,True)
+ self.res_set_end_NM_002129_4_end = (
+ 'ATATATATATATATATATAT',
+ 'ATATATATATATATATATATAT',
+ '','TA',1421,1441,True)
+
+ # del
+ self.NR_110010_2_del_result_set = (
+ 'GCGCGCG','GCGCG','CG','',25,32,True)
+ self.NR_110010_2_del_result_set_end = (
+ 'GCGCGCG','GCGCG','GC','',25,32,True)
+ self.NM_003073_5_del_result_set = (
+ 'AGAAGAAGA','AGAAGA','AGA','',1288,1297,True)
+ self.NM_002111_8_del_result_set = (
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 'CAG','',196,261,True)
+ self.NM_002111_8_del_result_set_end = (
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 'GCA','',196,261,True)
+ # start seq
+ # NM_001330096.1
+ # rep CT stop 26
+ # seq CTCTCTCTCTCTCTCTCTCTCTCTCT TTCTTTCTTTCCGATGGGGAAGAGAGGGT
+ # NM_001330112.1
+ # rep GGGGCGGGTC stop 28 (or 20 for complete segments)
+ # seq (with flank) GGGGCGGGTCGGGGCGGGTC GGGGCGGG CCCACGGGCGGCCGGATTTG
+ # end seq
+ # NM_052928.3
+ # rep GTTTT start 4375 len 4405
+ # seq (with flank):
+ # ATAACACATATGCCTCCTTCTGAGTTGTTG GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT
+ # NM_002129.4
+ # rep AT start 1408 len 1441 seq
+ # ATTAGCTAAATTGTTCCTCAGGTGTGTG T ATATATATATACATATATATATATATATATAT
+ self.res_set_d_start_NM_001330096_1 = (
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCT',
+ 'CTCTCTCTCTCTCTCTCTCTCTCT',
+ 'TC','',0,26,True)
+ self.res_set_d_start_NM_001330096_1_end = (
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCT',
+ 'CTCTCTCTCTCTCTCTCTCTCTCT',
+ 'CT','',0,26,True)
+ self.res_set_d_start_NM_001330112_1 = (
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ 'GGGGCGGGTCGGGGCGGG',
+ 'GGGGCGGGTC','',0,28,True)
+ self.res_set_d_start_NM_001330112_1_end = (
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ 'GGGGCGGGTCGGGGCGGG',
+ 'TCGGGGCGGG','',0,28,True)
+
+ self.res_set_d_end_NM_052928_3 = (
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ 'GTTTTGTTTTGTTTTGTTTTGTTTT',
+ 'GTTTT', '', 4375,4405,True)
+ self.res_set_d_end_NM_052928_3_end = (
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ 'GTTTTGTTTTGTTTTGTTTTGTTTT',
+ 'TTGTT', '', 4375,4405,True)
+ self.res_set_d_end_NM_002129_4 = (
+ 'ATATATATATATATATATAT',
+ 'ATATATATATATATATAT',
+ 'AT','',1421,1441,True)
+ self.res_set_d_end_NM_002129_4_end = (
+ 'ATATATATATATATATATAT',
+ 'ATATATATATATATATAT',
+ 'TA','',1421,1441,True)
+
+ def test_vrs_normalise_outwards(self):
+ # == return un-trimmed
+ res = self.vrs.normalise_outwards('NR_110010.2', 'CG','CG', 31,33)
+ NR_110010_2_eq_set = ('CG','CG','CG','CG',31,33,0)
+ assert res == NR_110010_2_eq_set
+
+ def test_vrs_normalise_outwards_ex_left_ins(self):
+ # expand left ins
+ res = self.vrs.normalise_outwards('NR_110010.2', '','CG', 32,32)
+ assert res == self.NR_110010_2_ins_result_set
+ res = self.vrs.normalise_outwards('NR_110010.2', 'G','GCG', 31,32)
+ assert res == self.NR_110010_2_ins_result_set_end
+ res = self.vrs.normalise_outwards('NM_003073.5', '','AGA', 1297,1297)
+ assert res == self.NM_003073_5_ins_result_set
+ res = self.vrs.normalise_outwards(
+ 'NM_003073.5', 'AGA','AGAAGA', 1294,1297)
+ assert res == self.NM_003073_5_ins_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', '','GCA', 261,261)
+ assert res == self.NM_002111_8_ins_result_set_end
+ res = self.vrs.normalise_outwards('NM_002111.8', 'CA','CAGCA', 259,261)
+ assert res == self.NM_002111_8_ins_result_set
+
+ def test_vrs_normalise_outwards_ex_right_ins(self):
+ # expand right ins
+ res = self.vrs.normalise_outwards('NR_110010.2', '','GC', 25,25)
+ assert res == self.NR_110010_2_ins_result_set_end
+ res = self.vrs.normalise_outwards('NR_110010.2', 'G','GCG', 25,26)
+ assert res == self.NR_110010_2_ins_result_set_end
+ res = self.vrs.normalise_outwards('NM_003073.5', '','AGA', 1288,1288)
+ assert res == self.NM_003073_5_ins_result_set
+ res = self.vrs.normalise_outwards(
+ 'NM_003073.5', 'AGA','AGAAGA', 1288,1291)
+ assert res == self.NM_003073_5_ins_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', '','CAG', 196,196)
+ assert res == self.NM_002111_8_ins_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', 'CA','CAGCA', 196,198)
+ assert res == self.NM_002111_8_ins_result_set
+
+ def test_vrs_normalise_outwards_ex_both_ins(self):
+ # expand both directions ins
+ res = self.vrs.normalise_outwards('NR_110010.2', '','GC', 27,27)
+ assert res == self.NR_110010_2_ins_result_set_end
+ res = self.vrs.normalise_outwards('NR_110010.2', 'G','GCG', 27,28)
+ assert res == self.NR_110010_2_ins_result_set_end
+ res = self.vrs.normalise_outwards('NM_003073.5', '','AGA', 1291,1291)
+ assert res == self.NM_003073_5_ins_result_set
+ res = self.vrs.normalise_outwards(
+ 'NM_003073.5', 'AGA','AGAAGA', 1291,1294)
+ assert res == self.NM_003073_5_ins_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', '','CAG', 202,202)
+ assert res == self.NM_002111_8_ins_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', 'CA','CAGCA', 202,204)
+ assert res == self.NM_002111_8_ins_result_set
+
+ def test_vrs_normalise_outwards_ex_done_ins(self):
+ # expand already done ins
+ res = self.vrs.normalise_outwards(
+ 'NR_110010.2', 'GCGCGCG','GCGCGCGCG', 25,32)
+ assert res == self.NR_110010_2_ins_result_set_end
+ res = self.vrs.normalise_outwards(
+ 'NM_003073.5', 'AGAAGAAGA','AGAAGAAGAAGA', 1288,1297)
+ assert res == self.NM_003073_5_ins_result_set
+ res = self.vrs.normalise_outwards(
+ 'NM_002111.8',
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 196,261)
+ assert res == self.NM_002111_8_ins_result_set
+
+ def test_vrs_normalise_outwards_ex_into_start_ins(self):
+ # expand into start ins
+ res = self.vrs.normalise_outwards('NM_001330096.1', '','CT', 24,24)
+ assert res == self.res_set_start_NM_001330096_1_end
+ res = self.vrs.normalise_outwards('NM_001330096.1', 'T','TCT', 23,24)
+ assert res == self.res_set_start_NM_001330096_1
+ res = self.vrs.normalise_outwards(
+ 'NM_001330112.1', '','GGGGCGGGTC', 10,10)
+ assert res == self.res_set_start_NM_001330112_1
+ res = self.vrs.normalise_outwards(
+ 'NM_001330112.1', 'TC','TCGGGGCGGGTC', 8,10)
+ assert res == self.res_set_start_NM_001330112_1_end
+
+ def test_vrs_normalise_outwards_ex_into_start_done_ins(self):
+ # expand start already done ins
+ res = self.vrs.normalise_outwards(
+ 'NM_001330096.1',
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCT',
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCTCT',
+ 0,26)
+ assert res == self.res_set_start_NM_001330096_1_end
+ res = self.vrs.normalise_outwards(
+ 'NM_001330112.1',
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ 0,28)
+ assert res == self.res_set_start_NM_001330112_1
+
+
+ def test_vrs_normalise_outwards_ex_into_end_ins(self):
+ # expand into end ins
+ res = self.vrs.normalise_outwards('NM_052928.3', '','GTTTT', 4380,4380)
+ assert res == self.res_set_end_NM_052928_3
+ res = self.vrs.normalise_outwards(
+ 'NM_052928.3', 'TT','TTGTTTT', 4378,4380)
+ assert res == self.res_set_end_NM_052928_3_end
+ res = self.vrs.normalise_outwards('NM_002129.4', '','TA', 1422,1422)
+ assert res == self.res_set_end_NM_002129_4_end
+ res = self.vrs.normalise_outwards('NM_002129.4', 'TA','TATA', 1422,1424)
+ assert res == self.res_set_end_NM_002129_4_end
+
+ def test_vrs_normalise_outwards_ex_into_end_done_ins(self):
+ # expand end already done ins
+ res = self.vrs.normalise_outwards(
+ 'NM_052928.3',
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ 4375,4405)
+ assert res == self.res_set_end_NM_052928_3
+ res = self.vrs.normalise_outwards(
+ 'NM_002129.4',
+ 'ATATATATATATATATATAT',
+ 'ATATATATATATATATATATAT',
+ 1421,1441)
+ assert res == self.res_set_end_NM_002129_4
+
+
+ ##DEL SET##
+ def test_vrs_normalise_outwards_ex_left_del(self):
+ # expand left del
+ res = self.vrs.normalise_outwards('NR_110010.2', 'CG','', 30,32)
+ assert res == self.NR_110010_2_del_result_set
+ res = self.vrs.normalise_outwards('NR_110010.2', 'GCG','G', 29,32)
+ assert res == self.NR_110010_2_del_result_set_end
+ res = self.vrs.normalise_outwards('NM_003073.5', 'AGA','', 1294,1297)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.vrs.normalise_outwards(
+ 'NM_003073.5', 'AGAAGA','AGA', 1291,1297)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', 'GCA','', 258,261)
+ assert res == self.NM_002111_8_del_result_set_end
+ res = self.vrs.normalise_outwards('NM_002111.8', 'CAGCA','CA', 256,261)
+ assert res == self.NM_002111_8_del_result_set
+
+ def test_vrs_normalise_outwards_ex_left_del_flank_sepfetch(self):
+ # repeat left shift with self.ex_short_vrs to test flank fetch with:
+ # minimised ref length > 2*flank fetch
+ # this is used as the cut-off for fetching flanks separately
+ res = self.ex_short_vrs.normalise_outwards(
+ 'NM_003073.5', 'AGA','', 1294,1297)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.ex_short_vrs.normalise_outwards(
+ 'NM_003073.5', 'AGAAGA','AGA', 1291,1297)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.ex_short_vrs.normalise_outwards(
+ 'NM_002111.8', 'GCA','', 258,261)
+ assert res == self.NM_002111_8_del_result_set_end
+ res = self.ex_short_vrs.normalise_outwards(
+ 'NM_002111.8', 'CAGCA','CA', 256,261)
+ assert res == self.NM_002111_8_del_result_set
+
+ def test_vrs_normalise_outwards_ex_right_del(self):
+ # expand right del
+ res = self.vrs.normalise_outwards('NR_110010.2', 'GC','', 25,27)
+ assert res == self.NR_110010_2_del_result_set_end
+ res = self.vrs.normalise_outwards('NR_110010.2', 'GCG','G', 25,28)
+ assert res == self.NR_110010_2_del_result_set_end
+ res = self.vrs.normalise_outwards('NM_003073.5', 'AGA','', 1288,1291)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.vrs.normalise_outwards(
+ 'NM_003073.5', 'AGAAGA','AGA', 1288,1294)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', 'CAG','', 196,199)
+ assert res == self.NM_002111_8_del_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', 'CAGCA','CA', 196,201)
+ assert res == self.NM_002111_8_del_result_set
+
+ def test_vrs_normalise_outwards_ex_right_del_flank_sepfetch(self):
+ # repeat left shift with self.ex_short_vrs to test flank fetch with:
+ # minimised ref length > 2*flank fetch
+ # this is used as the cut-off for fetching flanks separately
+ res = self.ex_short_vrs.normalise_outwards(
+ 'NM_003073.5', 'AGA','', 1288,1291)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.ex_short_vrs.normalise_outwards(
+ 'NM_003073.5', 'AGAAGA','AGA', 1288,1294)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.ex_short_vrs.normalise_outwards(
+ 'NM_002111.8', 'CAG','', 196,199)
+ assert res == self.NM_002111_8_del_result_set
+ res = self.ex_short_vrs.normalise_outwards(
+ 'NM_002111.8', 'CAGCA','CA', 196,201)
+ assert res == self.NM_002111_8_del_result_set
+
+ def test_vrs_normalise_outwards_ex_both_del(self):
+ # expand both directions del
+ res = self.vrs.normalise_outwards('NR_110010.2', 'GC','', 27,29)
+ assert res == self.NR_110010_2_del_result_set_end
+ res = self.vrs.normalise_outwards('NR_110010.2', 'GCG','G', 27,30)
+ assert res == self.NR_110010_2_del_result_set_end
+ res = self.vrs.normalise_outwards('NM_003073.5', 'AGA','', 1291,1294)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.vrs.normalise_outwards('NM_003073.5', 'AGAAGA','AGA', 1291,1297)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', 'CAG','', 202,205)
+ assert res == self.NM_002111_8_del_result_set
+ res = self.vrs.normalise_outwards('NM_002111.8', 'CAGCA','CA', 202,207)
+ assert res == self.NM_002111_8_del_result_set
+
+ def test_vrs_normalise_outwards_ex_done_del(self):
+ # expand already done del
+ res = self.vrs.normalise_outwards(
+ 'NR_110010.2', 'GCGCGCG','GCGCG', 25,32)
+ assert res == self.NR_110010_2_del_result_set_end
+ res = self.vrs.normalise_outwards(
+ 'NM_003073.5', 'AGAAGAAGA','AGAAGA', 1288,1297)
+ assert res == self.NM_003073_5_del_result_set
+ res = self.vrs.normalise_outwards(
+ 'NM_002111.8',
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 'CAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCAGCA',
+ 196,261)
+ assert res == self.NM_002111_8_del_result_set
+
+ def test_vrs_normalise_outwards_ex_into_start_del(self):
+ # expand into start del
+ res = self.vrs.normalise_outwards('NM_001330096.1', 'CT','', 22,24)
+ assert res == self.res_set_d_start_NM_001330096_1_end
+ res = self.vrs.normalise_outwards('NM_001330096.1', 'TCT','T', 21,24)
+ assert res == self.res_set_d_start_NM_001330096_1
+ res = self.vrs.normalise_outwards(
+ 'NM_001330112.1', 'GGGGCGGGTC','', 10,20)
+ assert res == self.res_set_d_start_NM_001330112_1
+ res = self.vrs.normalise_outwards(
+ 'NM_001330112.1', 'TCGGGGCGGGTC', 'TC', 8,20)
+ assert res == self.res_set_d_start_NM_001330112_1_end
+
+ def test_vrs_normalise_outwards_ex_into_start_done_del(self):
+ # expand start already done del
+ res = self.vrs.normalise_outwards(
+ 'NM_001330096.1',
+ 'CTCTCTCTCTCTCTCTCTCTCTCTCT',
+ 'CTCTCTCTCTCTCTCTCTCTCTCT',
+ 0,26)
+ assert res == self.res_set_d_start_NM_001330096_1_end
+ res = self.vrs.normalise_outwards(
+ 'NM_001330112.1',
+ 'GGGGCGGGTCGGGGCGGGTCGGGGCGGG',
+ 'GGGGCGGGTCGGGGCGGG',
+ 0,28)
+ assert res == self.res_set_d_start_NM_001330112_1
+
+ def test_vrs_normalise_outwards_ex_into_end_del(self):
+ # expand into end del
+ res = self.vrs.normalise_outwards('NM_052928.3', 'GTTTT','', 4380,4385)
+ assert res == self.res_set_d_end_NM_052928_3
+ res = self.vrs.normalise_outwards(
+ 'NM_052928.3', 'TTGTTTT','TT', 4378,4385)
+ assert res == self.res_set_d_end_NM_052928_3_end
+ res = self.vrs.normalise_outwards('NM_002129.4', 'TA','', 1422,1424)
+ assert res == self.res_set_d_end_NM_002129_4_end
+ res = self.vrs.normalise_outwards('NM_002129.4', 'TATA','TA', 1422,1426)
+ assert res == self.res_set_d_end_NM_002129_4_end
+
+ def test_vrs_normalise_outwards_ex_into_end_done_del(self):
+ # expand end already done del
+ res = self.vrs.normalise_outwards(
+ 'NM_052928.3',
+ 'GTTTTGTTTTGTTTTGTTTTGTTTTGTTTT',
+ 'GTTTTGTTTTGTTTTGTTTTGTTTT',
+ 4375,4405)
+ assert res == self.res_set_d_end_NM_052928_3
+ res = self.vrs.normalise_outwards(
+ 'NM_002129.4',
+ 'ATATATATATATATATATAT',
+ 'ATATATATATATATATAT',
+ 1421,1441)
+ assert res == self.res_set_d_end_NM_002129_4
+
+class TestVRSOutputVRSObjectIDs(unittest.TestCase):
+ """
+ Tests for the internal vrs_util.py module, particularly the HGVS_to_VRS
+ object functions required for fetching and making VRS IDs.
+
+ A good number of these tests take their required output from the VRS docs
+ (current as of VRS 2.1).
+ tests:
+ Test VRS IDs for all data types with specified id's in the VRS docs,
+ using stored VRS data as input
+ Test that the VRS id generation code produces the expected output for
+ the given input.
+ """
+ @classmethod
+ def setup_class(self):
+ self.vrs = HGVS_to_VRS()
+
+ def test_vrs_seq_ids_bad_id(self):
+ # test 1 bad ID to check that it asserts in some way
+ with self.assertRaises(KeyError):
+ self.vrs.id_fetch('LIVE_PARROT')
+
+ def test_vrs_seq_ids(self):
+ """
+ Test the basic fetching of VRS sequence IDs using given hgvs ID.
+ The VRS id_fetch() calls get_vrs_id_for_seq() to get the base id
+ via SeqRepo, this might change in the future, but we want to test the
+ id_fetch in use for this, not the base function, as the id_fetch
+ function in use is the target of these tests.
+ """
+ # the internal set of sequences used:
+ # NR_110010.2, NM_003073.5, NM_002111.8, NM_001350922.2,
+ # NM_001330096.1, NM_001330112.1, NM_052928.3, and NM_002129.4
+ vrs_seq_id = self.vrs.id_fetch('NR_110010.2')
+ assert vrs_seq_id == 'SQ.Ksaa229gzGC4Uc3ytCixai4vziud-MKi'
+ vrs_seq_id = self.vrs.id_fetch('NM_003073.5')
+ assert vrs_seq_id == 'SQ.pL2lXe9Qjg9ME_5vP0o85MgTUIqTAklc'
+ vrs_seq_id = self.vrs.id_fetch('NM_002111.8')
+ assert vrs_seq_id == 'SQ.4qBhnA470l_6xfLWJyi8WkOKeW6u5KJJ'
+ vrs_seq_id = self.vrs.id_fetch('NM_001350922.2')
+ assert vrs_seq_id == 'SQ.OgH_wLQV1lYs_Z107fts3xI9CuarHG7_'
+ vrs_seq_id = self.vrs.id_fetch('NM_001330096.1')
+ assert vrs_seq_id == 'SQ.Xzqbjy7R3hDF81IInpgNugs7N3c0qk2M'
+ vrs_seq_id = self.vrs.id_fetch('NM_001330112.1')
+ assert vrs_seq_id == 'SQ.L_1dwsmKTbVRGrbiF4p6FrgaWkWKWycu'
+ vrs_seq_id = self.vrs.id_fetch('NM_052928.3')
+ assert vrs_seq_id == 'SQ.pGqCl6h3ASKqxNA6o0aLmx_6NIoQio9l'
+ vrs_seq_id = self.vrs.id_fetch('NM_002129.4')
+ assert vrs_seq_id == 'SQ.8rOzThqGEimTT1vXu8KEb73YqgPBMfWJ'
+ # sequence IDs found in the vrs documentation
+ # allele docs
+ vrs_seq_id = self.vrs.id_fetch('NC_000001.11')
+ assert vrs_seq_id == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ # sequence docs and from:
+ # https://github.com/ga4gh/vrs/blob/2.0/validation/models.yaml
+ vrs_seq_id = self.vrs.id_fetch('NC_000007.14')
+ assert vrs_seq_id == "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul"
+
+ def test_vrs_flatten(self):
+ """ Test the VRS flattening logic for basic functionality and currently
+ unused but, in the future needed, behaviour """
+ # Test flattening start stop spans
+ flat = self.vrs.vrs_flatten({
+ "type": "SequenceLocation",
+ "sequenceReference": {
+ "type": "SequenceReference",
+ "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul"
+ },
+ "start": [44908820,44908821],
+ "end": [44908822,44908823]
+ })
+ assert flat == \
+ '{"end":"[44908822,44908823]","sequenceReference":{'\
+ '"refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul","type":"'\
+ 'SequenceReference"},"start":"[44908820,44908821]","type":'\
+ '"SequenceLocation"}'
+ flat = self.vrs.vrs_flatten({
+ "type": "SequenceLocation",
+ "sequenceReference": {
+ "type": "SequenceReference",
+ "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul"
+ },
+ "start": [None,44908821],
+ "end": [44908822,None]
+ })
+ assert flat == \
+ '{"end":"[44908822,null]","sequenceReference":{"refgetAccession":'\
+ '"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul","type":"SequenceReference"}'\
+ ',"start":"[null,44908821]","type":"SequenceLocation"}'
+ # Test specified digest_keys
+ flat = self.vrs.vrs_flatten({
+ "type": "SequenceReference",
+ "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul",
+ "test":"bla"
+ }, dict_keys=["refgetAccession","test","type"])
+ assert flat == \
+ '{"refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul","test":'\
+ '"bla","type":"SequenceReference"}'
+
+
+ def test_vrs_object_ids_assert_invalid_type(self):
+ # test type assert on flatten & hash on non valid data type
+ with self.assertRaises(TypeError):
+ vrs_eg_seq_loc = {
+ "type": "SequenceReference",
+ "refgetAccession": self.vrs
+ }
+ self.vrs.vrs_flatten(vrs_eg_seq_loc,digest=True)
+
+ def test_vrs_object_ids_assert_fail_no_vrs_id_prefix(self):
+ # test value error on VRS type without VRS id prefix
+ with self.assertRaises(ValueError):
+ vrs_state = {
+ "type": "ReferenceLengthExpression",
+ "length": 11,
+ "repeatSubunitLength": 3
+ }
+ self.vrs.vrs_flatten(vrs_state,digest=True)
+
+ # now test working ids
+ def test_vrs_object_ids_seq_loc(self):
+ vrs_eg_seq_loc = {
+ "id": "ga4gh:SL.4t6JnYWqHwYw9WzBT_lmWBb3tLQNalkT",
+ "type": "SequenceLocation",
+ "sequenceReference": {
+ "type": "SequenceReference",
+ "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul"
+ },
+ "start": 44908821,
+ "end": 44908822
+ }
+ vrs_id = self.vrs.vrs_flatten(vrs_eg_seq_loc,digest=True)
+ assert vrs_id == vrs_eg_seq_loc['id']
+
+ def test_vrs_object_ids_full_allele(self):
+ vrs_eg_allele = {
+ "id": "ga4gh:VA.Oop4kjdTtKcg1kiZjIJAAR3bp7qi4aNT",
+ "type": "Allele",
+ "expressions": [
+ {
+ "syntax": "spdi",
+ "value": "NC_000001.11:40819438:CTCCTCCT:CTCCTCCTCCT"
+ }
+ ],
+ "location": {
+ "type": "SequenceLocation",
+ "sequenceReference": {
+ "type": "SequenceReference",
+ "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
+ "residueAlphabet": "na",
+ "id": "NC_000001.11"
+ },
+ "start": 40819438,
+ "end": 40819446
+ },
+ "state": {
+ "type": "ReferenceLengthExpression",
+ "length": 11,
+ "repeatSubunitLength": 3
+ }
+ }
+ vrs_id = self.vrs.vrs_flatten(vrs_eg_allele,digest=True)
+ assert vrs_id == vrs_eg_allele['id']
+
+ def test_vrs_object_ids_allele_from_eg_page(self):
+ vrs_eg_allele_from_id_eg_page = {
+ "location": {
+ "type": "SequenceLocation",
+ "sequenceReference": {
+ "type": "SequenceReference",
+ "refgetAccession": "SQ.IIB53T8CNeJJdUqzn9V_JnRtQadwWCbl"
+ },
+ "start": 44908821,
+ "end": 44908822
+ },
+ "state": {
+ "type": "LiteralSequenceExpression",
+ "sequence": "T"
+ },
+ "type": "Allele"
+ }
+ #{"location":"wIlaGykfwHIpPY2Fcxtbx4TINbbODFVz","state":
+ # {"sequence":"T","type":"LiteralSequenceExpression"},"type":"Allele"}
+ # ga4gh:VA.0AePZIWZUNsUlQTamyLrjm2HWUw2opLt_
+ # note that the _ on the end is not genarated even when running the
+ # sample code given by
+ # https://vrs.ga4gh.org/en/latest/conventions/computed_identifiers.html#digest-serialization
+ # *exactly* as-is, the rest of the ID is identical
+ vrs_id = self.vrs.vrs_flatten(vrs_eg_allele_from_id_eg_page,digest=True)
+ assert vrs_id == 'ga4gh:VA.0AePZIWZUNsUlQTamyLrjm2HWUw2opLt'
+
+ def test_vrs_object_ids_b_moddels_notebook(self):
+ # test the ids from the basic models notebook
+ # https://github.com/ga4gh/vrs-python/blob/main/notebooks/getting_started/3_Basic_Models.ipynb
+ #'NM_002439.5'# SQ.Pw3Ch0x3XWD6ljsnIfmk_NERcZCI9sNM
+ vrs_eg_allele_from_basic_mod_nb = {
+ 'id': 'ga4gh:VA.5C67OBmCLuHPgDkCQj7EOMih58BS2Eor',
+ 'type': 'Allele',
+ 'location': {'type': 'SequenceLocation',
+ 'sequenceReference': {'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.Pw3Ch0x3XWD6ljsnIfmk_NERcZCI9sNM'},
+ 'start': 80656509,
+ 'end': 80656510},
+ 'state': {'type': 'LiteralSequenceExpression', 'sequence': 'TT'}}
+ vrs_id_loc = self.vrs.vrs_flatten(
+ vrs_eg_allele_from_basic_mod_nb['location'],digest=True)
+ assert vrs_id_loc == 'ga4gh:SL.lGxOP1JRd4dysmrOVaskO5P_35DyCLnx'
+ vrs_id = self.vrs.vrs_flatten(
+ vrs_eg_allele_from_basic_mod_nb,digest=True)
+ assert vrs_id == 'ga4gh:VA.5C67OBmCLuHPgDkCQj7EOMih58BS2Eor'
+
+ def test_vrs_object_ids_2_0_validation_models1(self):
+ # test id creation for Alleles and sequence loacations found in
+ # https://github.com/ga4gh/vrs/blob/2.0/validation/models.yaml
+ vrs_allele_valid_model_test_set_lit= {'type': 'Allele',
+ 'location': {'type': 'SequenceLocation',
+ 'sequenceReference': {'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.IIB53T8CNeJJdUqzn9V_JnRtQadwWCbl'},
+ 'start': 44908821,
+ 'end': 44908822},
+ 'state': {'type': 'LiteralSequenceExpression', 'sequence': 'T'}}
+ vrs_id = self.vrs.vrs_flatten(
+ vrs_allele_valid_model_test_set_lit['location'],digest=True)
+ assert vrs_id == 'ga4gh:SL.wIlaGykfwHIpPY2Fcxtbx4TINbbODFVz'
+ vrs_id = self.vrs.vrs_flatten(
+ vrs_allele_valid_model_test_set_lit,digest=True)
+ assert vrs_id == 'ga4gh:VA.0AePZIWZUNsUlQTamyLrjm2HWUw2opLt'
+
+ def test_vrs_object_ids_2_0_validation_models2(self):
+ # test id creation for Alleles and sequence loacations found in
+ # https://github.com/ga4gh/vrs/blob/2.0/validation/models.yaml
+ vrs_allele_valid_model_test_set_len= {'type': 'Allele',
+ 'location': {'type': 'SequenceLocation',
+ 'sequenceReference': {'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'},
+ 'start': 40819438,
+ 'end': 40819446},
+ 'state': {
+ 'type': 'ReferenceLengthExpression',
+ 'length': 11,
+ 'repeatSubunitLength': 3}}
+ vrs_id = self.vrs.vrs_flatten(
+ vrs_allele_valid_model_test_set_len['location'],digest=True)
+ assert vrs_id == 'ga4gh:SL.nQGBuvRQOLEboA5TYtcz975fp_GulxbZ'
+ vrs_id = self.vrs.vrs_flatten(
+ vrs_allele_valid_model_test_set_len,digest=True)
+ assert vrs_id == 'ga4gh:VA.Oop4kjdTtKcg1kiZjIJAAR3bp7qi4aNT'
+
+ def test_vrs_object_ids_notebook_ex_allele_translator1(self):
+ # https://github.com/ga4gh/vrs-python/blob/main/notebooks/getting_started/4_Exploring_the_AlleleTranslator.ipynb
+ hgvs_out_lit = {'type': 'Allele',
+ 'location': {'type': 'SequenceLocation',
+ 'sequenceReference': {'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.aUiQCzCPZ2d0csHbMSbh2NzInhonSXwI'},
+ 'start': 80656488,
+ 'end': 80656489},
+ 'state': {'type': 'LiteralSequenceExpression', 'sequence': 'T'}}
+ vrs_id = self.vrs.vrs_flatten(hgvs_out_lit['location'],digest=True)
+ assert vrs_id == 'ga4gh:SL.JiLRuuyS5wefF_6-Vw7m3Yoqqb2YFkss'
+ vrs_id = self.vrs.vrs_flatten(hgvs_out_lit,digest=True)
+ assert vrs_id == 'ga4gh:VA.ebezGL6HoAhtGJyVnB_mE5BH18ntKev4'
+
+ def test_vrs_object_ids_notebook_ex_allele_translator2(self):
+ # https://github.com/ga4gh/vrs-python/blob/main/notebooks/getting_started/4_Exploring_the_AlleleTranslator.ipynb
+ hgvs_out_lit2 = {'type': 'Allele',
+ 'location': {'type': 'SequenceLocation',
+ 'sequenceReference': {'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.vbjOdMfHJvTjK_nqvFvpaSKhZillW0SX'},
+ 'start': 79952307,
+ 'end': 79952308},
+ 'state': {'type': 'LiteralSequenceExpression', 'sequence': 'T'}}
+ vrs_id = self.vrs.vrs_flatten(hgvs_out_lit2['location'],digest=True)
+ assert vrs_id == 'ga4gh:SL.Y-itBtqe9IwbxyL4EVZ4T_X9TUsdbJ22'
+ vrs_id = self.vrs.vrs_flatten(hgvs_out_lit2,digest=True)
+ assert vrs_id == 'ga4gh:VA.hEyB1sGiQrdrPFIq4u4CF17uAuUs2Wvx'
+
+class TestVRSOutputVRSObjects(unittest.TestCase):
+ """
+ Tests for the internal vrs_util.py module, particularly the HGVS_to_VRS
+ object function hgvs_single_var_to_vrs, which is the the basic core
+ hgvs->vrs function.
+
+ Starts with standard input as from
+ https://github.com/ga4gh/vrs/blob/2.0/validation/models.yaml
+ then moves to extra tests on shortcuts and error/null return states.
+
+ """
+ @classmethod
+ def setup_class(self):
+ self.vrs = HGVS_to_VRS()
+
+ def test_vrs_object_output_NC_000019_10_g_44908822C_T_set(self):
+
+ # allele test
+ # hgvs in NC_000019.10:g44908822_44908821C>T
+ hgvs = vv.hp.parse('NC_000019.10:g.44908822C>T')
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 44908822
+ assert hgvs_allele['location']['start'] == 44908821
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.IIB53T8CNeJJdUqzn9V_JnRtQadwWCbl'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.wIlaGykfwHIpPY2Fcxtbx4TINbbODFVz'
+ assert hgvs_allele['state']['sequence'] == 'T'
+ assert hgvs_allele['id'] == 'ga4gh:VA.0AePZIWZUNsUlQTamyLrjm2HWUw2opLt'
+
+ def test_vrs_object_output_NC_000001_11_g_40819439_40819446delCTCCTCCTinsCTCCTCCTCCT(self):
+ # No need to test the 'canonical' JSON serilised form as this, is not available as output,
+ # and should be redundant with the checksum ID test
+ # hgvs in "NC_000001.11:40819439_40819446delCTCCTCCTinsCTCCTCCTCCT
+ hgvs = vv.hp.parse(
+ "NC_000001.11:g.40819439_40819446delCTCCTCCTinsCTCCTCCTCCT")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819446
+ assert hgvs_allele['location']['start'] == 40819438
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.nQGBuvRQOLEboA5TYtcz975fp_GulxbZ'
+ assert hgvs_allele['state']['length'] == 11
+ assert hgvs_allele['state']['repeatSubunitLength'] == 3
+ assert hgvs_allele['id'] == 'ga4gh:VA.Oop4kjdTtKcg1kiZjIJAAR3bp7qi4aNT'
+
+ def test_vrs_object_output_NC_000005_10_g_80656489C_T(self):
+ # test for good output from hgvs input matching
+ # https://github.com/ga4gh/vrs-python/blob/main/notebooks/getting_started/4_Exploring_the_AlleleTranslator.ipynb
+ #From HGVS
+ hgvs = vv.hp.parse("NC_000005.10:g.80656489C>T")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 80656489
+ assert hgvs_allele['location']['start'] == 80656488
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.aUiQCzCPZ2d0csHbMSbh2NzInhonSXwI'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.JiLRuuyS5wefF_6-Vw7m3Yoqqb2YFkss'
+ assert hgvs_allele['state']['sequence'] == 'T'
+ assert hgvs_allele['id'] == 'ga4gh:VA.ebezGL6HoAhtGJyVnB_mE5BH18ntKev4'
+
+ def test_vrs_object_output_NC_000005_9_g_79952308C_T(self):
+ hgvs = vv.hp.parse("NC_000005.9:g.79952308C>T")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 79952308
+ assert hgvs_allele['location']['start'] == 79952307
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.vbjOdMfHJvTjK_nqvFvpaSKhZillW0SX'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.Y-itBtqe9IwbxyL4EVZ4T_X9TUsdbJ22'
+ assert hgvs_allele['state']['sequence'] == 'T'
+ assert hgvs_allele['id'] == 'ga4gh:VA.hEyB1sGiQrdrPFIq4u4CF17uAuUs2Wvx'
+
+
+ def test_vrs_obj_out_norm_identity_diff_input_start(self):
+ """
+ test VRS IDs are the same for multiple hgvs inputs that should
+ normalise to the same output VRS start hgvs input.
+ """
+ # hgvs in "NC_000001.11:40819439_40819446delCTCCTCCTinsCTCCTCCTCCT
+ hgvs = vv.hp.parse("NC_000001.11:g.40819438_40819439insCTC")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819446
+ assert hgvs_allele['location']['start'] == 40819438
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.nQGBuvRQOLEboA5TYtcz975fp_GulxbZ'
+ assert hgvs_allele['state']['length'] == 11
+ assert hgvs_allele['state']['repeatSubunitLength'] == 3
+ assert hgvs_allele['id'] == 'ga4gh:VA.Oop4kjdTtKcg1kiZjIJAAR3bp7qi4aNT'
+
+ def test_vrs_obj_out_norm_identity_diff_input_end(self):
+ """
+ test VRS IDs are the same for multiple hgvs inputs that should
+ normalise to the same output VRS end hgvs input.
+ """
+ # hgvs in "NC_000001.11:40819439_40819446delCTCCTCCTinsCTCCTCCTCCT
+ hgvs = vv.hp.parse("NC_000001.11:g.40819446_40819447insCCT")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819446
+ assert hgvs_allele['location']['start'] == 40819438
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.nQGBuvRQOLEboA5TYtcz975fp_GulxbZ'
+ assert hgvs_allele['state']['length'] == 11
+ assert hgvs_allele['state']['repeatSubunitLength'] == 3
+ assert hgvs_allele['id'] == 'ga4gh:VA.Oop4kjdTtKcg1kiZjIJAAR3bp7qi4aNT'
+
+ def test_vrs_obj_out_norm_identity_diff_input_mid(self):
+ """
+ test VRS IDs are the same for multiple hgvs inputs that should
+ normalise to the same output VRS middle hgvs input.
+ """
+ # hgvs in "NC_000001.11:40819439_40819446delCTCCTCCTinsCTCCTCCTCCT
+ hgvs = vv.hp.parse("NC_000001.11:g.40819443_40819444insCCT")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819446
+ assert hgvs_allele['location']['start'] == 40819438
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.nQGBuvRQOLEboA5TYtcz975fp_GulxbZ'
+ assert hgvs_allele['state']['length'] == 11
+ assert hgvs_allele['state']['repeatSubunitLength'] == 3
+ assert hgvs_allele['id'] == 'ga4gh:VA.Oop4kjdTtKcg1kiZjIJAAR3bp7qi4aNT'
+
+ def test_vrs_obj_out_norm_identity_diff_set_dup(self):
+ """
+ test VRS IDs are the same for multiple hgvs inputs that should
+ normalise to the same output VRS middle hgvs input.
+ Dup, like ins also has a separate code path from del/delins/subtitue
+ test this as well.
+ """
+ # hgvs in "NC_000001.11:40819439_40819446delCTCCTCCTinsCTCCTCCTCCT
+ hgvs = vv.hp.parse("NC_000001.11:g.40819442_40819444dup")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819446
+ assert hgvs_allele['location']['start'] == 40819438
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.nQGBuvRQOLEboA5TYtcz975fp_GulxbZ'
+ assert hgvs_allele['state']['length'] == 11
+ assert hgvs_allele['state']['repeatSubunitLength'] == 3
+ assert hgvs_allele['id'] == 'ga4gh:VA.Oop4kjdTtKcg1kiZjIJAAR3bp7qi4aNT'
+
+ def test_vrs_object_output_part_ambig_pos(self):
+ """
+ test variant with partially ambiguous positions
+ eg ACCA to ACGCCA could be an ins either before (of CG) or after
+ (of GC) the fist C
+ so VRS expands to cover the first C
+ hgvs inputs should be normalised 3' in this case, but this also should
+ work either way
+ done for TCCT >TCGCCT and TCCT >TCCGCT in fully expanded delins + both
+ directions
+ """
+ hgvs = vv.hp.parse("NC_000001.11:g.40819444_40819445delCCinsCCGC")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819445
+ assert hgvs_allele['location']['start'] == 40819444
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.UhfMqgh_jnDrVNWaFx-_ksYssV1-9Hr3'
+ assert hgvs_allele['state']['sequence'] == 'CGC'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['id'] == 'ga4gh:VA.r-IMwTjeSjYhwqkaIX7tloTeeMnuEGYy'
+
+ hgvs = vv.hp.parse("NC_000001.11:g.40819445delCinsCGC")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819445
+ assert hgvs_allele['location']['start'] == 40819444
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.UhfMqgh_jnDrVNWaFx-_ksYssV1-9Hr3'
+ assert hgvs_allele['state']['sequence'] == 'CGC'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['id'] == 'ga4gh:VA.r-IMwTjeSjYhwqkaIX7tloTeeMnuEGYy'
+
+
+ # extra tests VRS varified, outside of the examples?
+
+ def test_vrs_object_output_eq_shortcut(self):
+ "hgvs_single_var_to_vrs on == variant to test internal shortcut"
+ hgvs = vv.hp.parse("NC_000001.11:g.40819444_40819445=")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819445
+ assert hgvs_allele['location']['start'] == 40819443
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.zxbyl3-hn1MHmcZYF0N8WWaAzNZcryGk'
+ assert hgvs_allele['state']['length'] == 2
+ assert hgvs_allele['state']['repeatSubunitLength'] == 2
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+
+ def test_vrs_object_output_fail_with_no_mapper_for_c_input(self):
+ # Test C type works, and failes propperly with mapper missing
+ with self.assertRaises(ValueError):
+ self.vrs.hgvs_single_var_to_vrs(
+ vv.hp.parse('NM_015120.4:c.35T>C'))
+
+ def test_vrs_object_output_for_c_input(self):
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(
+ vv.hp.parse('NM_015120.4:c.35T>C'),
+ variant = MockVVData())
+ assert hgvs_allele == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.mq1Saz29534GzZ6T_jCfUjsqNlifC7pZ',
+ 'label': 'NM_015120.4'
+ },
+ 'start': 145,
+ 'end': 146,
+ 'id': 'ga4gh:SL.x_rdZXUcg7V8Ae5pPVv0OS-faxKyzNQb'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'C'
+ },
+ 'id': 'ga4gh:VA.x8gKEBS-s638D8uuuQXVJKIgCDcuQJCW'
+ }
+
+ def test_vrs_object_output_no_norm_shortcut_ins(self):
+ # test that ins and del that don't at all normalise outwards
+ # use the shortcut correctly
+ hgvs = vv.hp.parse("NC_000001.11:g.40819444_40819445insG")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819444
+ assert hgvs_allele['location']['start'] == 40819444
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.aBJ1_py6od8pI4OWHQegaTxdAsszkbBy'
+ assert hgvs_allele['state']['sequence'] == 'G'
+ assert hgvs_allele['state']['type'] == 'LiteralSequenceExpression'
+ assert hgvs_allele['id'] == 'ga4gh:VA.i_1aVFr-IQvl8bXoIX_UjwAU6_IwAmCD'
+
+ def test_vrs_object_output_no_norm_shortcut_del(self):
+ hgvs = vv.hp.parse("NC_000001.11:g.40819444_40819445delCC")
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819445
+ assert hgvs_allele['location']['start'] == 40819443
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.zxbyl3-hn1MHmcZYF0N8WWaAzNZcryGk'
+ assert hgvs_allele['state']['repeatSubunitLength'] == 2
+ assert hgvs_allele['state']['length'] == 0
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['id'] == 'ga4gh:VA.gpJtRDCma3r-7snQ90wFU7i-hRPuJKGh'
+
+ def test_vrs_object_output_intronic_null_result(self):
+ # test null result on intronic input
+ hgvs_allele = self.vrs.hgvs_single_var_to_vrs(
+ # not valid but works for test
+ vv.hp.parse('NM_015120.4:c.35-6T>C'),
+ variant = MockVVData())
+ assert hgvs_allele is None
+
+ def test_vrs_object_output_cache(self):
+ # test cached = type
+ vrs_cached = copy.copy(self.vrs)
+ vrs_cached.cache = {}
+ hgvs = vv.hp.parse("NC_000001.11:g.40819444_40819445=")
+ hgvs_allele = vrs_cached.hgvs_single_var_to_vrs(hgvs)
+ assert hgvs_allele['type'] == 'Allele'
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert hgvs_allele['location']['type'] == 'SequenceLocation'
+ assert hgvs_allele['location']['sequenceReference'][
+ 'type'] == 'SequenceReference'
+ assert hgvs_allele['location']['end'] == 40819445
+ assert hgvs_allele['location']['start'] == 40819443
+ assert hgvs_allele['location']['sequenceReference'][
+ 'refgetAccession'] == 'SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO'
+ assert hgvs_allele['location'][
+ 'id'] == 'ga4gh:SL.zxbyl3-hn1MHmcZYF0N8WWaAzNZcryGk'
+ assert hgvs_allele['state']['length'] == 2
+ assert hgvs_allele['state']['repeatSubunitLength'] == 2
+ assert hgvs_allele['state']['type'] == 'ReferenceLengthExpression'
+ assert vrs_cached.cache["NC_000001.11:g.40819444_40819445="] == \
+ hgvs_allele
+
+class TestVRSOutputFromDummyVV(unittest.TestCase):
+ """
+ Test the generation of VRS output from dummy VV result objects, unlike raw
+ VV this relies objects this does not rely on VV working consistently with
+ previous results. Unfortunately this also means that this test set may need
+ to be changed in the case of VV alterations/improvements.
+
+ See also the direct VV using tests below in the case of failures.
+ """
+
+ @classmethod
+ def setup_class(self):
+ self.vrs = HGVS_to_VRS()
+ # core set of dummy input
+ self.dummy_vv_output = MockVVData(
+ selected_assembly='GRCh38',
+ original = 'test_input',
+ warnings=[],
+ lovd_messages = [],
+ lovd_corrections = [],
+ stable_gene_ids = {
+ 'hgnc_id': 'HGNC:TST',
+ 'entrez_gene_id': '0000',
+ 'ucsc_id': 'uc0tst.1',
+ 'omim_id': ['0000000']},
+ gene_symbol = 'Dummy_S',
+ description = "Text transcript description",)
+
+ def test_vv_output_to_vrs_output_val_err(self):
+ # test set 1: test that we trap and return variants with corrections
+ # without VRS data
+ # 1a test variant.warnings == ['Validation error'] response
+ err_fail = copy.copy(self.dummy_vv_output)
+ err_fail.warnings=['Validation error']
+ output = self.vrs.variant_validator_output_set_to_vrs(err_fail)
+ assert output['selected_assembly'] == err_fail.selected_assembly
+ assert output['submitted_variant'] == err_fail.original
+ assert output['warnings_and_messages'] == {
+ 'validation_warnings': ['Validation error'],
+ 'lovd_messages': [],
+ 'lovd_corrections': []
+ }
+ assert output['gene_ids']['hgnc_id'] == 'HGNC:TST'
+ assert output['gene_ids']['entrez_gene_id'] == '0000'
+ assert output['gene_ids']['ucsc_id'] == 'uc0tst.1'
+ assert output['gene_ids']['omim_id'] == ['0000000']
+ assert output['gene_ids']['current_symbol'] == 'Dummy_S'
+ assert output['transcript_description'] == err_fail.description
+
+ def test_vv_output_to_vrs_output_warn_type(self):
+ # 1b test variant.output_type_flag = 'warning'
+ err_warn = copy.copy(self.dummy_vv_output)
+ err_warn.output_type_flag = 'warning'
+ output = self.vrs.variant_validator_output_set_to_vrs(err_warn)
+ assert output['selected_assembly'] == err_warn.selected_assembly
+ assert output['submitted_variant'] == err_warn.original
+ assert output['warnings_and_messages'] == {
+ 'validation_warnings': [],
+ 'lovd_messages': [],
+ 'lovd_corrections': []
+ }
+ assert output['gene_ids']['hgnc_id'] == 'HGNC:TST'
+ assert output['gene_ids']['entrez_gene_id'] == '0000'
+ assert output['gene_ids']['ucsc_id'] == 'uc0tst.1'
+ assert output['gene_ids']['omim_id'] == ['0000000']
+ assert output['gene_ids']['current_symbol'] == 'Dummy_S'
+ assert output['transcript_description'] == err_warn.description
+
+ def test_vv_output_to_vrs_output_warn_for_working_type(self):
+ # test warnings in output for variants with output_type_flag and w/o
+ err_warn = copy.copy(self.dummy_vv_output)
+ err_warn.output_type_flag = 'warning'
+ err_warn.warnings=['VV_Warn']
+ err_warn.lovd_messages = ['LO_Warn']
+ err_warn.lovd_corrections = ['LO_corr']
+ output = self.vrs.variant_validator_output_set_to_vrs(err_warn)
+ assert output['selected_assembly'] == err_warn.selected_assembly
+ assert output['submitted_variant'] == err_warn.original
+ assert output['warnings_and_messages'] == {
+ 'validation_warnings': ['VV_Warn'],
+ 'lovd_messages': ['LO_Warn'],
+ 'lovd_corrections': ['LO_corr']
+ }
+ assert output['gene_ids']['hgnc_id'] == 'HGNC:TST'
+ assert output['gene_ids']['entrez_gene_id'] == '0000'
+ assert output['gene_ids']['ucsc_id'] == 'uc0tst.1'
+ assert output['gene_ids']['omim_id'] == ['0000000']
+ assert output['gene_ids']['current_symbol'] == 'Dummy_S'
+ assert output['transcript_description'] == err_warn.description
+
+ def test_vv_output_to_vrs_output_gene_symbol(self):
+ # test that gene symbols get handled regardless of stable gene id state
+ no_gene_id = copy.copy(self.dummy_vv_output)
+ no_gene_id.stable_gene_ids = None
+ output = self.vrs.variant_validator_output_set_to_vrs(no_gene_id)
+ assert output['gene_ids'] == {'current_symbol': 'Dummy_S'}
+ no_gene_id.gene_symbol = None
+ output = self.vrs.variant_validator_output_set_to_vrs(no_gene_id)
+ assert output['gene_ids'] is None
+
+ # seq tests done using data from input test 1
+ def test_vv_output_to_vrs_output_r_type(self):
+ # test 2: test that we catch and return the simpler r type input
+ # 2a test for working output to valid input
+ rna_test = copy.copy(self.dummy_vv_output)
+ rna_test.rna_data = {
+ 'rna_variant':vv.hp.parse('NM_015120.4:c.35T>C'),
+ 'translation_slr':vv.hp.parse('NP_055935.4:p.(L12P)'),
+ 'usage_warnings':"RNA warning"}
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(rna_test)
+ assert vrs_out['vrs_transcript_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.mq1Saz29534GzZ6T_jCfUjsqNlifC7pZ',
+ 'label': 'NM_015120.4'
+ },
+ 'start': 145,
+ 'end': 146,
+ 'id': 'ga4gh:SL.x_rdZXUcg7V8Ae5pPVv0OS-faxKyzNQb'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'C'
+ },
+ 'id': 'ga4gh:VA.x8gKEBS-s638D8uuuQXVJKIgCDcuQJCW'
+ }
+ assert vrs_out['vrs_predicted_protein_consequence'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.ggxH8oENu3WDprE6Z_Qg1HvOiT0d8UNa',
+ 'label': 'NP_055935.4'
+ },
+ 'start': 11,
+ 'end': 12,
+ 'id': 'ga4gh:SL.qOpPs9dwgqK6MuH3CTQUDoRYbi9IWwSh'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'P'
+ },
+ 'id': 'ga4gh:VA.cJPulDXE1G73sZS4dHaZX_27-c4pNjYo'
+ }
+
+ def test_vv_output_to_vrs_output_r_type_missmatch1(self):
+ # 2b test that r type variant causes assertions on
+ # hgvs_transcript_variant or primary_assembly_loci being set
+ # (we don't check object type)
+ rna_test_bad_1 = copy.copy(self.dummy_vv_output)
+ rna_test_bad_1.rna_data = {
+ 'rna_variant':vv.hp.parse('NM_015120.4:c.35T>C'),
+ 'translation_slr':vv.hp.parse('NP_055935.4:p.(L12P)'),
+ 'usage_warnings':"RNA warning"}
+ rna_test_bad_1.hgvs_transcript_variant = 1
+ with self.assertRaises(Exception):
+ self.vrs.variant_validator_output_set_to_vrs(rna_test_bad_1)
+
+ def test_vv_output_to_vrs_output_r_type_missmatch2(self):
+ rna_test_bad_2 = copy.copy(self.dummy_vv_output)
+ rna_test_bad_2.rna_data = {
+ 'rna_variant':vv.hp.parse('NM_015120.4:c.35T>C'),
+ 'translation_slr':vv.hp.parse('NP_055935.4:p.(L12P)'),
+ 'usage_warnings':"RNA warning"}
+ rna_test_bad_2.primary_assembly_loci = 2
+ with self.assertRaises(Exception):
+ self.vrs.variant_validator_output_set_to_vrs(rna_test_bad_2)
+
+ def test_vv_output_to_vrs_output_working_exonic(self):
+ # test 3: test for working responses to c/n type input that is exonic
+ c_test = copy.copy(self.dummy_vv_output)
+ c_test.hgvs_transcript_variant = vv.hp.parse('NM_015120.4:c.35T>C')
+ hgvs_hg37 = vv.hp.parse('NC_000002.11:g.73613031delinsCGGA')
+ hgvs_hg38 = vv.hp.parse('NC_000002.12:g.73385903delinsCGGA')
+ c_test.primary_assembly_loci = {
+ 'hg19': {
+ 'hgvs_genomic_description': hgvs_hg37,
+ 'vcf': {
+ 'chr': 'chr2', 'pos': '73613031', 'ref': 'T', 'alt': 'CGGA'}
+ },
+ 'hg38': {
+ 'hgvs_genomic_description': hgvs_hg37,
+ 'vcf': {
+ 'chr': 'chr2', 'pos': '73385903', 'ref': 'T', 'alt': 'CGGA'}
+ },
+ 'grch37' : {
+ 'hgvs_genomic_description': hgvs_hg37,
+ 'vcf': {
+ 'chr': '2', 'pos': '73613031', 'ref': 'T', 'alt': 'CGGA'}
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': hgvs_hg38,
+ 'vcf': {
+ 'chr': '2', 'pos': '73385903', 'ref': 'T', 'alt': 'CGGA'}
+ }
+ }
+ c_test.hgvs_predicted_protein_consequence = {
+ 'prot':vv.hp.parse('NP_055935.4:p.(L12P)')}
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(c_test)
+ assert vrs_out['vrs_transcript_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.mq1Saz29534GzZ6T_jCfUjsqNlifC7pZ',
+ 'label': 'NM_015120.4'
+ },
+ 'start': 145,
+ 'end': 146,
+ 'id': 'ga4gh:SL.x_rdZXUcg7V8Ae5pPVv0OS-faxKyzNQb'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'C'
+ },
+ 'id': 'ga4gh:VA.x8gKEBS-s638D8uuuQXVJKIgCDcuQJCW'
+ }
+ assert vrs_out['vrs_predicted_protein_consequence'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.ggxH8oENu3WDprE6Z_Qg1HvOiT0d8UNa',
+ 'label': 'NP_055935.4'
+ },
+ 'start': 11,
+ 'end': 12,
+ 'id': 'ga4gh:SL.qOpPs9dwgqK6MuH3CTQUDoRYbi9IWwSh'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'P'
+ },
+ 'id': 'ga4gh:VA.cJPulDXE1G73sZS4dHaZX_27-c4pNjYo'
+ }
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == {
+ 'grch37': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.9KdcA9ZpY1Cpvxvg8bMSLYDUpsX6GDLO',
+ 'label': 'NC_000002.11'
+ },
+ 'start': 73613030,
+ 'end': 73613031,
+ 'id': 'ga4gh:SL.IzA6GY6mvDiJ1EzMbe1Cxv6jyhg4u8t7'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CGGA'
+ },
+ 'id': 'ga4gh:VA.rKc_HxeylEKYQzYo_e_kY1lqYLF6Ch5m'
+ },
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.pnAqCRBrTsUoBghSD1yp_jXWSmlbdh4g',
+ 'label': 'NC_000002.12'
+ },
+ 'start': 73385902,
+ 'end': 73385903,
+ 'id': 'ga4gh:SL.ndPBndyntJTTRHuU6tYpgbN6TUsRv94B'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CGGA'
+ },
+ 'id': 'ga4gh:VA.eDiJCZTUpLf1UQHaUFFP1UaPsHfbOjyC'
+ }
+ }
+ def test_vv_output_to_vrs_output_working_exonic_w_rsg(self):
+ c_test = copy.copy(self.dummy_vv_output)
+ c_test.hgvs_transcript_variant = vv.hp.parse('NM_015120.4:c.35T>C')
+ c_test.hgvs_refseqgene_variant = vv.hp.parse('NG_011690.1:g.5146T>C')
+ hgvs_hg37 = vv.hp.parse('NC_000002.11:g.73613031delinsCGGA')
+ hgvs_hg38 = vv.hp.parse('NC_000002.12:g.73385903delinsCGGA')
+ c_test.primary_assembly_loci = {
+ 'hg19': {'hgvs_genomic_description': hgvs_hg37},
+ 'hg38': {'hgvs_genomic_description': hgvs_hg37},
+ 'grch37' : {'hgvs_genomic_description': hgvs_hg37},
+ 'grch38': {'hgvs_genomic_description': hgvs_hg38}
+ }
+ c_test.hgvs_predicted_protein_consequence = {
+ 'prot':vv.hp.parse('NP_055935.4:p.(L12P)')}
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(c_test)
+ assert vrs_out['vrs_transcript_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.mq1Saz29534GzZ6T_jCfUjsqNlifC7pZ',
+ 'label': 'NM_015120.4'
+ },
+ 'start': 145,
+ 'end': 146,
+ 'id': 'ga4gh:SL.x_rdZXUcg7V8Ae5pPVv0OS-faxKyzNQb'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'C'
+ },
+ 'id': 'ga4gh:VA.x8gKEBS-s638D8uuuQXVJKIgCDcuQJCW'
+ }
+ assert vrs_out['vrs_predicted_protein_consequence'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.ggxH8oENu3WDprE6Z_Qg1HvOiT0d8UNa',
+ 'label': 'NP_055935.4'
+ },
+ 'start': 11,
+ 'end': 12,
+ 'id': 'ga4gh:SL.qOpPs9dwgqK6MuH3CTQUDoRYbi9IWwSh'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'P'
+ },
+ 'id': 'ga4gh:VA.cJPulDXE1G73sZS4dHaZX_27-c4pNjYo'
+ }
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == {
+ 'grch37': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.9KdcA9ZpY1Cpvxvg8bMSLYDUpsX6GDLO',
+ 'label': 'NC_000002.11'
+ },
+ 'start': 73613030,
+ 'end': 73613031,
+ 'id': 'ga4gh:SL.IzA6GY6mvDiJ1EzMbe1Cxv6jyhg4u8t7'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CGGA'
+ },
+ 'id': 'ga4gh:VA.rKc_HxeylEKYQzYo_e_kY1lqYLF6Ch5m'
+ },
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.pnAqCRBrTsUoBghSD1yp_jXWSmlbdh4g',
+ 'label': 'NC_000002.12'
+ },
+ 'start': 73385902,
+ 'end': 73385903,
+ 'id': 'ga4gh:SL.ndPBndyntJTTRHuU6tYpgbN6TUsRv94B'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CGGA'
+ },
+ 'id': 'ga4gh:VA.eDiJCZTUpLf1UQHaUFFP1UaPsHfbOjyC'
+ }
+ }
+ assert vrs_out['vrs_refseqgene_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.r47Jw6neAxxIQgeyZLmdPrqFN4S6h7hy',
+ 'label': 'NG_011690.1'},
+ 'start': 5145,
+ 'end': 5146,
+ 'id': 'ga4gh:SL.3CEnQ3JJo2jQbraJ0UHIO-lqBbR4xs15'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'C'},
+ 'id': 'ga4gh:VA.TjhuWhUG-PJapHphK3r0HWyU5b9CgGG_'}
+
+ def test_vv_output_to_vrs_output_intronic_main_chr(self):
+ # test 4: test for working responses to c/n type input that is intronic
+ # 4a intronic matches main genomic ref, taken from input test 7
+ c_int_test = copy.copy(self.dummy_vv_output)
+ c_int_test.hgvs_transcript_variant = vv.hp.parse(
+ 'NM_000548.4:c.138+821del')
+ c_int_test.genome_context_intronic_sequence = copy.copy(
+ c_int_test.hgvs_transcript_variant)
+ c_int_test.genome_context_intronic_sequence.rel_ac = 'NC_000016.9'
+ c_int_test.hgvs_predicted_protein_consequence = {
+ 'prot': vv.hp.parse('NP_000539.2:p.?')}
+
+ hgvs_hg37 = vv.hp.parse('NC_000016.9:g.2099575del')
+ hgvs_hg38 = vv.hp.parse('NC_000016.10:g.2049574del')
+ c_int_test.primary_assembly_loci = {
+ 'hg19':{'hgvs_genomic_description': hgvs_hg37},
+ 'hg38':{'hgvs_genomic_description': hgvs_hg38},
+ 'grch37':{'hgvs_genomic_description': hgvs_hg37},
+ 'grch38':{'hgvs_genomic_description': hgvs_hg38}}
+ c_int_test.hgvs_genomic = copy.copy(hgvs_hg37)
+
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(c_int_test)
+ warnings_and_messages = {
+ 'validation_warnings': [],
+ 'lovd_messages': [],
+ 'lovd_corrections': [],
+ 'vrs_output_warnings': [
+ 'VRSIntronWarning: VRS does not handle mappings shared between '
+ 'genomic and transcript reference sequences. As such only the '
+ 'genomic mappings for this hgvs intronic transcript variant are'
+ ' preserved.']}
+ vrs_intronic_genomic_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.W6wLoIFOn4G7cjopxPxYNk2lcEqhLQFb',
+ 'label': 'NC_000016.9'},
+ 'start': 2099572,
+ 'end': 2099575,
+ 'id': 'ga4gh:SL.OSRCAmPHLKeVCIccBJw2PE2fjBBZVO5g'},
+ 'state': {
+ 'type': 'ReferenceLengthExpression',
+ 'length': 2, 'repeatSubunitLength': 1},
+ 'id': 'ga4gh:VA.R4sVDvRvhoB_h0TTNcxbRkEkqL8Iwzww'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch37': vrs_intronic_genomic_variant,
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.yC_0RBj3fgBlvgyAuycbzdubtLxq-rE0',
+ 'label': 'NC_000016.10'},
+ 'start': 2049571,
+ 'end': 2049574,
+ 'id': 'ga4gh:SL.LBYz9IIcNSR00VhmUb06RRpf9WQKh_xN'},
+ 'state': {
+ 'type': 'ReferenceLengthExpression',
+ 'length': 2,
+ 'repeatSubunitLength': 1},
+ 'id': 'ga4gh:VA.swtaZ9h8m_gc6bDXPtNAALWSRBquhDcs'}}
+ assert vrs_out['warnings_and_messages']['vrs_output_warnings'] == \
+ warnings_and_messages['vrs_output_warnings']
+ assert vrs_out['warnings_and_messages'] == warnings_and_messages
+ assert vrs_out['vrs_intronic_genomic_variant'] == \
+ vrs_intronic_genomic_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+
+ def test_vv_output_to_vrs_output_intronic_alt_chr_match(self):
+ # 4b intronic matches alt genomic ref, adapted from input test 189
+ # also tests RSG+ intronic RSG
+ c_int_alt = copy.copy(self.dummy_vv_output)
+ c_int_alt.hgvs_transcript_variant = vv.hp.parse(
+ 'NM_012309.4:c.913-5058G>A')
+ # orig 'genome_context_intronic_sequence' = \
+ # 'NC_000011.10(NM_012309.4):c.913-5058G>A'
+ # we switch to NW_004070871.1 which would normally only turn up in
+ # output if used in input to test such input handling
+ c_int_alt.hgvs_transcript_variant.rel_ac = 'NW_004070871.1'
+ c_int_alt.refseqgene_context_intronic_sequence = vv.hp.parse(
+ 'NM_012309.4:c.913-5058G>A')
+ c_int_alt.refseqgene_context_intronic_sequence.rel_ac = 'NG_042866.1'
+ c_int_alt.hgvs_refseqgene_variant = vv.hp.parse(
+ 'NG_042866.1:g.149464G>A')
+ c_int_alt.hgvs_predicted_protein_consequence = {
+ 'prot': vv.hp.parse('NP_036441.2:p.?')}
+ hgvs_hg37_alt = vv.hp.parse('NW_004070871.1:g.574546C>T')
+ c_int_alt.alt_genomic_loci = [{
+ 'grch37': {
+ 'hgvs_genomic_description': hgvs_hg37_alt,
+ }}, {
+ 'hg19': {
+ 'hgvs_genomic_description': hgvs_hg37_alt,
+ }}]
+ hgvs_hg37 = vv.hp.parse('NC_000011.10:g.71080333C>T')
+ c_int_alt.primary_assembly_loci = {
+ 'hg38': {'hgvs_genomic_description': hgvs_hg37},
+ 'grch38': {'hgvs_genomic_description': hgvs_hg37}}
+ c_int_alt.hgvs_genomic = hgvs_hg37_alt
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(c_int_alt)
+ warnings_and_messages = {
+ 'validation_warnings': [],
+ 'lovd_messages': [],
+ 'lovd_corrections': [],
+ 'vrs_output_warnings': [
+ 'VRSIntronWarning: VRS does not handle mappings shared '
+ 'between genomic and transcript reference sequences. As '
+ 'such only the genomic mappings for this hgvs intronic '
+ 'transcript variant are preserved.']}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1',
+ 'label': 'NC_000011.10'},
+ 'start': 71080332,
+ 'end': 71080333,
+ 'id': 'ga4gh:SL.s01bDxQQkHA1YafVKjPKqmPdy_BCgwWp'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.Exskx3X6aUlO_msp7n5jyBYUDkiHG2Xw'}}
+ VRS_mappings_for_alt_genomic_loci = [{
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.EZG3Q7xvM2XQ-HQTX5w1BEEmWo-AGqtB',
+ 'label': 'NW_004070871.1'},
+ 'start': 574545,
+ 'end': 574546,
+ 'id': 'ga4gh:SL.c9V2DUbRs4P15CWnthnRRgptVohYcy__'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.BzVOTYucxGEVNsslkihaJ_kgH9GN5Tro'}]
+ vrs_intronic_genomic_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.EZG3Q7xvM2XQ-HQTX5w1BEEmWo-AGqtB',
+ 'label': 'NW_004070871.1'},
+ 'start': 574545,
+ 'end': 574546,
+ 'id': 'ga4gh:SL.c9V2DUbRs4P15CWnthnRRgptVohYcy__'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.BzVOTYucxGEVNsslkihaJ_kgH9GN5Tro'}
+ assert vrs_out['warnings_and_messages'] == warnings_and_messages
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == \
+ VRS_mappings_for_alt_genomic_loci
+ assert vrs_out['vrs_intronic_genomic_variant'] == \
+ vrs_intronic_genomic_variant
+
+ def test_vv_output_to_vrs_output_intergenic_type_data(self):
+ # test 5 g type input (intergenic/psudo-intergenic variant input)
+ # taken from input test 1
+ intergenic_vv_res = copy.copy(self.dummy_vv_output)
+ # submitted_variant NC_000017.10:g.48279242G>T
+ intergenic_vv_res.hgvs_refseqgene_variant = vv.hp.parse(
+ 'NG_007400.1:g.4759C>A')
+ hgvs_hg37 = vv.hp.parse('NC_000017.10:g.48279242G>T')
+ hgvs_hg38 = vv.hp.parse('NC_000017.11:g.50201881G>T')
+ intergenic_vv_res.hgvs_genomic = hgvs_hg38
+ intergenic_vv_res.primary_assembly_loci = {
+ 'hg19': {'hgvs_genomic_description': hgvs_hg37},
+ 'hg38': {'hgvs_genomic_description': hgvs_hg38},
+ 'grch37': {'hgvs_genomic_description': hgvs_hg37},
+ 'grch38': {'hgvs_genomic_description': hgvs_hg38}}
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(intergenic_vv_res)
+ warnings_and_messages = {
+ 'validation_warnings': [],
+ 'lovd_messages': [],
+ 'lovd_corrections': []}
+ vrs_refseqgene_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.QHD1sq0MO0ekfVC3dyTMD76jTTuJdHzC',
+ 'label': 'NG_007400.1'},
+ 'start': 4758,
+ 'end': 4759,
+ 'id': 'ga4gh:SL.Zo3m-4l8X4ec1R9WJoFNnRZJpsf1vxD-'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'A'},
+ 'id': 'ga4gh:VA.T6RVcuFrzTqxCS16D3stnBB8uaf86LoT'}
+ VRS_mapping_for_grch37 = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.AjWXsI7AkTK35XW9pgd3UbjpC3MAevlz',
+ 'label': 'NC_000017.10'},
+ 'start': 48279241,
+ 'end': 48279242,
+ 'id': 'ga4gh:SL.BTi2Q9MAcw-eIZjsascBF4kPH7JFHiwK'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.Mu21lzIQgKp1Bzq0-E-DiVNNVS1GeF9l'}
+ VRS_mapping_for_grch38 = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.dLZ15tNO1Ur0IcGjwc3Sdi_0A6Yf4zm7',
+ 'label': 'NC_000017.11'},
+ 'start': 50201880,
+ 'end': 50201881,
+ 'id': 'ga4gh:SL.4sKmwc3lN8mn6DxNKFmV_piFt6zW4nkD'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.VMA149-dG25GHnAHd76yLApQ_5lMS2-4'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch37': VRS_mapping_for_grch37,
+ 'grch38': VRS_mapping_for_grch38}
+ assert vrs_out['vrs_intergenic_genomic_variant'] == \
+ VRS_mapping_for_grch38
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == []
+ assert vrs_out['warnings_and_messages'] == warnings_and_messages
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+ assert vrs_out['vrs_refseqgene_variant'] == vrs_refseqgene_variant
+
+ def test_vv_output_to_vrs_output_intronic(self):
+ # test variant.hgvs_refseqgene_variant used for output with transcript data
+ ex_vv_res = copy.copy(self.dummy_vv_output)
+ ex_vv_res.hgvs_transcript_variant = vv.hp.parse(
+ 'NM_000548.3:c.138+821del')
+ ex_vv_res.hgvs_refseqgene_variant = vv.hp.parse(
+ 'NG_005895.1:g.5269del')
+ hgvs_hg37 = vv.hp.parse('NC_000016.9:g.2099575del')
+ ex_vv_res.selected_assembly = 'GRCh37'
+ ex_vv_res.primary_assembly_loci = {
+ 'hg19': {'hgvs_genomic_description': hgvs_hg37},
+ 'grch37': {'hgvs_genomic_description': hgvs_hg37}}
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(ex_vv_res)
+ print(vrs_out)
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == { 'grch37': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.W6wLoIFOn4G7cjopxPxYNk2lcEqhLQFb',
+ 'label': 'NC_000016.9'},
+ 'start': 2099572,
+ 'end': 2099575,
+ 'id': 'ga4gh:SL.OSRCAmPHLKeVCIccBJw2PE2fjBBZVO5g'},
+ 'state': {
+ 'type': 'ReferenceLengthExpression',
+ 'length': 2,
+ 'repeatSubunitLength': 1},
+ 'id': 'ga4gh:VA.R4sVDvRvhoB_h0TTNcxbRkEkqL8Iwzww'}}
+
+ assert vrs_out['vrs_refseqgene_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.Zuvuz5j8xEuvGT-iyjeCPW96y9eJ-2Rt',
+ 'label': 'NG_005895.1'},
+ 'start': 5266,
+ 'end': 5269,
+ 'id': 'ga4gh:SL.7lSDh7A9L3fdqd91RzMX5JcLXzKoRL4m'},
+ 'state': {
+ 'type': 'ReferenceLengthExpression',
+ 'length': 2, 'repeatSubunitLength': 1},
+ 'id': 'ga4gh:VA.3-TnA4NFzRb1rPNumnbdwe6vzFK93Dy3'}
+
+class TestVRSOutputRangeTypeFromVV(unittest.TestCase):
+ """
+ Test VRS range response to VV uncertain location input types.
+ """
+ @classmethod
+ def setup_class(self):
+ self.vrs = HGVS_to_VRS()
+ # core set of dummy input
+ self.dummy_vv_output = MockVVData(
+ selected_assembly='GRCh38',
+ original = 'test_input',
+ warnings=[],
+ lovd_messages = [],
+ lovd_corrections = [],
+ stable_gene_ids = {
+ 'hgnc_id': 'HGNC:TST',
+ 'entrez_gene_id': '0000',
+ 'ucsc_id': 'uc0tst.1',
+ 'omim_id': ['0000000']},
+ gene_symbol = 'Dummy_S',
+ description = "Text transcript description",)
+ def _two_span_var(self, ac, var_type, s1, e1, s2, e2, edit):
+ if var_type in ['g','m','o']:
+ edit = vv.hp.parse_dna_edit(edit)
+ else:
+ edit = vv.hp.parse_rna_edit(edit)
+ o_start_pos, start_end = _hgvs_offset_pos_from_str_in(
+ s1,
+ None,
+ ref_type=var_type,
+ end=e1)
+ start = Interval(start=o_start_pos,end=start_end)
+ end_start, o_end_pos = _hgvs_offset_pos_from_str_in(
+ s2,
+ None,
+ ref_type=var_type,
+ end=e2)
+ end = Interval(start=end_start,end=o_end_pos)
+ full_obj = hgvs_obj_from_existing_edit(
+ ac,
+ var_type,
+ FEInterval(
+ start=start,
+ end=end),
+ edit)
+ return full_obj
+
+ # possible data for additional tests?
+ #SequenceLocation (redundant data == to below skipped)
+ # in:
+ # end: [44908822, 44908922]
+ # start: [44908721, 44908821]
+ # out:
+ # ga4gh_digest: 8-sGv9AY7GJT6QVgqbxhMXFNamnWcFJu
+ # ga4gh_identify: ga4gh:SL.8-sGv9AY7GJT6QVgqbxhMXFNamnWcFJu
+ # ga4gh_serialize: '{"end":[44908822,44908922],"sequenceReference":{"refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul","type":"SequenceReference"},"start":[44908721,44908821],"type":"SequenceLocation"}'
+ # - name: "SequenceLocation w/Definite and Indefinite Ranges"
+ # in:
+ # end: [44908822, null]
+ # start: [44908721, 44908821]
+ # out:
+ # ga4gh_digest: XQAXpesghmuDHziAcDCAmESBOPKTBhwD
+ # ga4gh_identify: ga4gh:SL.XQAXpesghmuDHziAcDCAmESBOPKTBhwD
+ # ga4gh_1_3_identify: ga4gh:VSL.zGh9Zy42Zu9R0sbyB1rXsxd33BIyiORk
+ # ga4gh_serialize: '{"end":[44908822,null],"sequenceReference":{"refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul","type":"SequenceReference"},"start":[44908721,44908821],"type":"SequenceLocation"}'
+ # ga4gh_1_3_serialize: '{"interval":{"end":{"comparator":">=","type":"IndefiniteRange","value":44908822},"start":{"max":44908821,"min":44908721,"type":"DefiniteRange"},"type":"SequenceInterval"},"sequence_id":"F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul","type":"SequenceLocation"}'
+ # - name: "SequenceLocation w/more Definite and Indefinite Ranges"
+ # in:
+ # end: [null, 44908822]
+ # start: [44908721, 44908821]
+ # out:
+ # ga4gh_digest: OYplG0vkUojmK2hDejylSykx-np3HPFP
+ # ga4gh_identify: ga4gh:SL.OYplG0vkUojmK2hDejylSykx-np3HPFP
+ # ga4gh_serialize: '{"end":[null,44908822],"sequenceReference":{"refgetAccession":"SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul","type":"SequenceReference"},"start":[44908721,44908821],"type":"SequenceLocation"}'
+
+ def test_vrs_range_unc_del(self):
+ # test unknown range input for del ins and dup
+ # 'NM_001377405.1:c.(4_246)delN[15]'
+ # "NC_000003.12:g.(63912602_63912844)delNNNNNNNNNNNNNNN"
+ tx_unk_var = vv.hp.parse('NM_001377405.1:c.(4_246)delNNNNNNNNNNNNNNN')
+ gen_unk_var = vv.hp.parse(
+ "NC_000003.12:g.(63912602_63912844)delNNNNNNNNNNNNNNN")
+ unc_test = copy.copy(self.dummy_vv_output)
+ unc_test.hgvs_transcript_variant = tx_unk_var
+ unc_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(unc_test)
+ warnings_and_messages = {
+ 'validation_warnings': [],
+ 'lovd_messages': [],
+ 'lovd_corrections': []}
+ assert vrs_out['warnings_and_messages'] == warnings_and_messages
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.xVHtmFKoGeLwmbDXLl_nxw0q4c2FRMYg',
+ 'label': 'NM_001377405.1'},
+ 'start': [405, 648],
+ 'end': [405, 648],
+ 'id': 'ga4gh:SL.wV0h-QOnWPUSj3T9ImdcphfFnqveZIMq'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [0,228]},# this would better be something like -15
+ # but does not translate well into VRS
+ 'id': 'ga4gh:VA.EpZ3bKdZlmN7K4mmRaLPtXavbriZJCT5'}
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.Zu7h9AggXxhTaGVsy7h_EZSChSZGcmgX',
+ 'label': 'NC_000003.12'},
+ 'start': [63912601,63912844],
+ 'end': [63912601,63912844],
+ 'id': 'ga4gh:SL.u2JpyMzpWPiqASuoO6yvXH_615hlTpRQ'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [0,228]},# as before this is the post-change state
+ # 15 bases del from unc pos maps badly
+ 'id': 'ga4gh:VA.Nt3i0Fc2JYErd93LznLg1G-l3hYTSN80'}}
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+ VRS_mappings_for_alt_genomic_loci = []
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] ==\
+ VRS_mappings_for_alt_genomic_loci
+
+ def test_vrs_range_unc_ins(self):
+ # now test ins delins dup and sub
+ # 'NM_001377405.1:c.(4_246)insTTTT'
+ # "NC_000003.12:g.(63912602_63912844)insTTT"
+ tx_unk_var = vv.hp.parse('NM_001377405.1:c.(4_246)insTTTT')
+ gen_unk_var = vv.hp.parse("NC_000003.12:g.(63912602_63912844)insTTTT")
+ unc_test = copy.copy(self.dummy_vv_output)
+ unc_test.hgvs_transcript_variant = tx_unk_var
+ unc_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(unc_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.xVHtmFKoGeLwmbDXLl_nxw0q4c2FRMYg',
+ 'label': 'NM_001377405.1'},
+ 'start': [405, 648],
+ 'end': [405, 648],
+ 'id': 'ga4gh:SL.wV0h-QOnWPUSj3T9ImdcphfFnqveZIMq'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'TTTT'},
+ 'id': 'ga4gh:VA.HLa3ZXHcJk2LPUFAX8d7qYE-NJcIVl0D'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.Zu7h9AggXxhTaGVsy7h_EZSChSZGcmgX',
+ 'label': 'NC_000003.12'},
+ 'start': [63912601, 63912844],
+ 'end': [63912601, 63912844],
+ 'id': 'ga4gh:SL.u2JpyMzpWPiqASuoO6yvXH_615hlTpRQ'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'TTTT'},
+ 'id': 'ga4gh:VA.KsKPm1k9vo1xoWj2AsKbzWrXzcKZ_rdG'}}
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] ==\
+ VRS_mappings_for_primary_assemblies
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == []
+
+ def test_vrs_range_unc_delins(self):
+ # 'NM_001377405.1:c.(4_246)delinsTTTT'
+ # "NC_000003.12:g.(63912602_63912844)delinsTTT"
+ tx_unk_var = vv.hp.parse('NM_001377405.1:c.(4_246)delinsTTTT')
+ gen_unk_var = vv.hp.parse(
+ "NC_000003.12:g.(63912602_63912844)delinsTTTT")
+ unc_test = copy.copy(self.dummy_vv_output)
+ unc_test.hgvs_transcript_variant = tx_unk_var
+ unc_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(unc_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.xVHtmFKoGeLwmbDXLl_nxw0q4c2FRMYg',
+ 'label': 'NM_001377405.1'},
+ 'start': [405, 648],
+ 'end': [405, 648],
+ 'id': 'ga4gh:SL.wV0h-QOnWPUSj3T9ImdcphfFnqveZIMq'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'TTTT'},
+ 'id': 'ga4gh:VA.HLa3ZXHcJk2LPUFAX8d7qYE-NJcIVl0D'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.Zu7h9AggXxhTaGVsy7h_EZSChSZGcmgX',
+ 'label': 'NC_000003.12'},
+ 'start': [63912601, 63912844],
+ 'end': [63912601, 63912844],
+ 'id': 'ga4gh:SL.u2JpyMzpWPiqASuoO6yvXH_615hlTpRQ'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'TTTT'},
+ 'id': 'ga4gh:VA.KsKPm1k9vo1xoWj2AsKbzWrXzcKZ_rdG'}}
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == []
+
+ def test_vrs_range_unc_dup(self):
+ # 'NM_001377405.1:c.(4_246)dup'
+ # "NC_000003.12:g.(63912602_63912844)dup"
+ tx_unk_var = vv.hp.parse('NM_001377405.1:c.(4_246)dup')
+ gen_unk_var = vv.hp.parse("NC_000003.12:g.(63912602_63912844)dup")
+ unc_test = copy.copy(self.dummy_vv_output)
+ unc_test.hgvs_transcript_variant = tx_unk_var
+ unc_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(unc_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.xVHtmFKoGeLwmbDXLl_nxw0q4c2FRMYg',
+ 'label': 'NM_001377405.1'},
+ 'start': [405, 648],
+ 'end': [405, 648],
+ 'id': 'ga4gh:SL.wV0h-QOnWPUSj3T9ImdcphfFnqveZIMq'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [0, 486]},
+ 'id': 'ga4gh:VA.oqDJt3ITorI0hrvsS7LmBUx-mBtQZRS7'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.Zu7h9AggXxhTaGVsy7h_EZSChSZGcmgX',
+ 'label': 'NC_000003.12'},
+ 'start': [63912601, 63912844],
+ 'end': [63912601, 63912844],
+ 'id': 'ga4gh:SL.u2JpyMzpWPiqASuoO6yvXH_615hlTpRQ'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [0, 486]},
+ 'id': 'ga4gh:VA.gtya_8q_3eubU0Hijsw_bR1DNU3KoPo4'}}
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == []
+
+ def test_vrs_range_unc_basechanage(self):
+ # 'NM_001377405.1:c.(4_246)C>G' # probably nonsense but we don't
+ # (can't) normalise so it works to test the code
+ # "NC_000003.12:g.(63912602_63912844)C>G"
+ tx_unk_var = vv.hp.parse('NM_001377405.1:c.(4_246)C>G')
+ gen_unk_var = vv.hp.parse("NC_000003.12:g.(63912602_63912844)C>G")
+ unc_test = copy.copy(self.dummy_vv_output)
+ unc_test.hgvs_transcript_variant = tx_unk_var
+ unc_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(unc_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.xVHtmFKoGeLwmbDXLl_nxw0q4c2FRMYg',
+ 'label': 'NM_001377405.1'},
+ 'start': [405, 648],
+ 'end': [405, 648],
+ 'id': 'ga4gh:SL.wV0h-QOnWPUSj3T9ImdcphfFnqveZIMq'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'G'},
+ 'id': 'ga4gh:VA.6r6uUT5Xopvs9mzMgnoLGnxn0UozdQ69'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.Zu7h9AggXxhTaGVsy7h_EZSChSZGcmgX',
+ 'label': 'NC_000003.12'},
+ 'start': [63912601, 63912844],
+ 'end': [63912601, 63912844],
+ 'id': 'ga4gh:SL.u2JpyMzpWPiqASuoO6yvXH_615hlTpRQ'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'G'},
+ 'id': 'ga4gh:VA.p4PEk1oAj25G_7C81VjYTNUmzxvgA1pu'}}
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == []
+
+ # test range input with spans for each end
+ # taken from test_uncertain_3
+ # 'NM_006138.4:c.(1_20)_(30_36)del'
+ # "grch38" 'NC_000011.10:g.(60061161_60061180)_(60061190_60061196)del'
+
+ def test_vrs_range_unc_span_intronic(self):
+ # test that intronic detection works on span of span types
+ tx_unk_var_intr = self._two_span_var(
+ 'NM_006138.4','c','1','20','30','36','del')
+ tx_unk_var_intr.posedit.pos.start.start.offset = True
+ assert self.vrs._intronic(tx_unk_var_intr)
+
+ def test_vrs_range_unc_span_del(self):
+ #'NM_006138.4:c.(1_20)_(30_36)del'
+ tx_unk_var = self._two_span_var(
+ 'NM_006138.4','c','1','20','30','36','del')
+ gen_unk_var = self._two_span_var(
+ 'NC_000011.10','g',
+ '60061161','60061180',
+ '60061190','60061196',
+ 'del')
+ unc_multi_end_test = copy.copy(self.dummy_vv_output)
+ unc_multi_end_test.hgvs_transcript_variant = tx_unk_var
+ unc_multi_end_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(unc_multi_end_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.kDpc3QS9BP_MPB7-8c32tLm_ofkpKuki',
+ 'label': 'NM_006138.4'},
+ 'start': [128, 148],
+ 'end': [157, 164],
+ 'id': 'ga4gh:SL.VKym0ZocRoqcMIFALbtocM9G-JXI_NPq'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [9, 36]},
+ 'id': 'ga4gh:VA._QbcvpURg3S57MKEBF5kNXJagBt8LD3Q'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1',
+ 'label': 'NC_000011.10'},
+ 'start': [60061160, 60061180],
+ 'end': [60061189, 60061196],
+ 'id': 'ga4gh:SL.3SYax-eEvXhMFCS3jWUrqpOQmBwbt5Xd'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [9, 36]},
+ 'id': 'ga4gh:VA.BimsbJi2CRmY5zw8dTShvosXgdW4KQGf'}}
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == VRS_mappings_for_primary_assemblies
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == []
+
+ def test_vrs_range_unc_span_delins(self):
+ #'NM_006138.4:c.(1_30)_(20_36)delinsCCC'
+ tx_unk_var = self._two_span_var(
+ 'NM_006138.4','c','1','20','30','36','delinsCCC')
+ gen_unk_var = self._two_span_var(
+ 'NC_000011.10','g',
+ '60061161','60061180',
+ '60061190','60061196',
+ 'delinsCCC')
+ unc_multi_end_test = copy.copy(self.dummy_vv_output)
+ unc_multi_end_test.hgvs_transcript_variant = tx_unk_var
+ unc_multi_end_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(
+ unc_multi_end_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.kDpc3QS9BP_MPB7-8c32tLm_ofkpKuki',
+ 'label': 'NM_006138.4'},
+ 'start': [128, 148],
+ 'end': [157, 164],
+ 'id': 'ga4gh:SL.VKym0ZocRoqcMIFALbtocM9G-JXI_NPq'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CCC'},
+ 'id': 'ga4gh:VA.hJy6_vMrmQUVQQzu3ZBmPboypcESYKmk'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1',
+ 'label': 'NC_000011.10'},
+ 'start': [60061160, 60061180],
+ 'end': [60061189, 60061196],
+ 'id': 'ga4gh:SL.3SYax-eEvXhMFCS3jWUrqpOQmBwbt5Xd'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CCC'},
+ 'id': 'ga4gh:VA.W4I8QnB7DOZzL5LFvdVLwWeEI0BdEWvX'}}
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == []
+
+ def test_vrs_range_unc_span_ins(self):
+ #ins
+ #'NM_006138.4:c.(1_30)_(20_36)insCCC'
+ tx_unk_var = self._two_span_var(
+ 'NM_006138.4','c','1','30','20','36','insCCC')
+ gen_unk_var = self._two_span_var(
+ 'NC_000011.10','g',
+ '60061161','60061190',
+ '60061180','60061196',
+ 'insCCC')
+ unc_multi_end_test = copy.copy(self.dummy_vv_output)
+ unc_multi_end_test.hgvs_transcript_variant = tx_unk_var
+ unc_multi_end_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(
+ unc_multi_end_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.kDpc3QS9BP_MPB7-8c32tLm_ofkpKuki',
+ 'label': 'NM_006138.4'},
+ 'start': [128, 158],
+ 'end': [147, 164],
+ 'id': 'ga4gh:SL.aXpOvtXB05WvJoNnaJEw8XCUJFKJfSRT'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CCC'},
+ 'id': 'ga4gh:VA.mTaxUtyrZx27pR7KgWFH392KzesV6PNm'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1',
+ 'label': 'NC_000011.10'},
+ 'start': [60061160, 60061190],
+ 'end': [60061179, 60061196],
+ 'id': 'ga4gh:SL.HJElyJSuBdeXx6zYpvI6kl2gRrmYYYAw'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CCC'},
+ 'id': 'ga4gh:VA.zqcJ7xJM5ucY5wAEEBf-VWcqnyRinBL8'}}
+
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+
+
+ def test_vrs_range_unc_span_dup(self):
+ #'NM_006138.4:c.(1_30)_(20_36)dup'
+ tx_unk_var = self._two_span_var(
+ 'NM_006138.4','c','1','20','30','36','dup')
+ gen_unk_var = self._two_span_var(
+ 'NC_000011.10','g',
+ '60061161','60061180',
+ '60061190','60061196',
+ 'dup')
+ unc_multi_end_test = copy.copy(self.dummy_vv_output)
+ unc_multi_end_test.hgvs_transcript_variant = tx_unk_var
+ unc_multi_end_test.primary_assembly_loci = {
+ 'hg38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ },
+ 'grch38': {
+ 'hgvs_genomic_description': gen_unk_var,
+ }
+ }
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(
+ unc_multi_end_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.kDpc3QS9BP_MPB7-8c32tLm_ofkpKuki',
+ 'label': 'NM_006138.4'},
+ 'start': [128, 148],
+ 'end': [157, 164],
+ 'id': 'ga4gh:SL.VKym0ZocRoqcMIFALbtocM9G-JXI_NPq'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [18, 72]},
+ 'id': 'ga4gh:VA.QldDqxFkU7wNQbB9QJvfiR3apKiWd25L'}
+ VRS_mappings_for_primary_assemblies = {
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1',
+ 'label': 'NC_000011.10'},
+ 'start': [60061160, 60061180],
+ 'end': [60061189, 60061196],
+ 'id': 'ga4gh:SL.3SYax-eEvXhMFCS3jWUrqpOQmBwbt5Xd'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [18, 72]},
+ 'id': 'ga4gh:VA.6_mydc1dT4GgT4lH7hsj1kzL7CuyHi41'}}
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert vrs_out['VRS_mappings_for_primary_assemblies'] == \
+ VRS_mappings_for_primary_assemblies
+ assert vrs_out['VRS_mappings_for_alt_genomic_loci'] == []
+
+ def test_vrs_range_unc_span_r_type_rna_var(self):
+ # test same for r. type variants
+ # prot is dummy but same type as expected
+ # note that r type are actually C or n type as far as biocommons.hgvs
+ # is concerned, r is used as similar to ":n.", but without caring about
+ # coding status
+ rna_test = copy.copy(self.dummy_vv_output)
+ tx_unk_var = self._two_span_var(
+ 'NM_006138.4','c','1','20','30','36','dup')
+ rna_test.rna_data = {
+ 'rna_variant':tx_unk_var,
+ 'translation_slr':vv.hp.parse('NP_055935.4:p.?'),
+ 'usage_warnings':"RNA warning"}
+ vrs_out = self.vrs.variant_validator_output_set_to_vrs(rna_test)
+ vrs_transcript_variant = {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.kDpc3QS9BP_MPB7-8c32tLm_ofkpKuki',
+ 'label': 'NM_006138.4'},
+ 'start': [128, 148],
+ 'end': [157, 164],
+ 'id': 'ga4gh:SL.VKym0ZocRoqcMIFALbtocM9G-JXI_NPq'},
+ 'state': {
+ 'type': 'LengthExpression',
+ 'length': [18, 72]},
+ 'id': 'ga4gh:VA.QldDqxFkU7wNQbB9QJvfiR3apKiWd25L'}
+ print(vrs_out)
+ assert vrs_out['vrs_transcript_variant'] == vrs_transcript_variant
+ assert 'VRS_mappings_for_primary_assemblies' not in vrs_out
+ assert 'VRS_mappings_for_alt_genomic_loci' not in vrs_out
+
+class TestVRSObjectInit(unittest.TestCase):
+ """
+ Test all remaining untested object init features, these are not normally
+ exposed by VV, so may be more subject to change than might otherwise be
+ expected.
+ """
+
+ def test_vrs_object_seqrepo(self):
+ # test setting SR loc
+ config = ConfigParser()
+ config.read(CONFIG_DIR)
+ seq_repo_path = os.path.join(config["seqrepo"]["location"],
+ config["seqrepo"]["version"])
+ vrs = HGVS_to_VRS(seq_repo=SeqRepo(seq_repo_path))
+ #do ID fetch based on set seqrepo as above
+ vrs_seq_id = vrs.id_fetch('NR_110010.2')
+ assert vrs_seq_id == 'SQ.Ksaa229gzGC4Uc3ytCixai4vziud-MKi'
+
+ # there is an additional (for now untested fallback for when
+ # VariantValidator.settings import CONFIG_DIR
+ # fails, but testing this here is hard, and since VV can't work in this case
+ # is redundant
+
+ def test_vrs_object_init_id_fetch(self):
+ # test setting ID fetch method
+ def _test_id_fetch(input_id):
+ if input_id == 'test_true':
+ return 'True'
+ return False
+ vrs = HGVS_to_VRS(id_fetch=_test_id_fetch)
+ vrs_seq_id = vrs.id_fetch('NR_110010.2')
+ assert vrs_seq_id is False
+ vrs_seq_id = vrs.id_fetch('test_true')
+ assert vrs_seq_id == 'True'
+
+ def test_vrs_object_init_preset_cache(self):
+ # dummy set a pre-filled cache with normally used and filled with
+ # previous hgvs sourced VRS output, due to how common repeats VV output
+ # can be(multi transcript but same genomic), but with weird contents
+ # for testing. Test via hgvs_single_var_to_vrs().
+ vrs = HGVS_to_VRS(cache = {'not_hgvs':{'not_vrs'}})
+ vrs_out = vrs.hgvs_single_var_to_vrs('not_hgvs')
+ assert vrs_out == {'not_vrs'}
+ assert vrs.cache == {'not_hgvs':{'not_vrs'}}
+
+ def test_vrs_object_init_normal_cache(self):
+ # test normal cache behaviour
+ vrs = HGVS_to_VRS(cache=True)
+ assert vrs.cache == {}
+
+class TestVRSObjectVV(unittest.TestCase):
+ """
+ Tests for VV usage of the code working as intended, should mostly be a
+ simple shim on vv_output_to_vrs_output internally so not a complex test set
+ at the moment.
+
+ Testing transcript, intronic transcript, genomic, and error type results
+ """
+ @classmethod
+ def setup_class(self):
+ self.vv = Validator()
+ self.vv.testing = True
+
+ def test_format_as_vrs_tx(self):
+ variant = 'NM_015120.4:c.35T>C'
+ results = self.vv.validate(variant, 'GRCh37', 'all')
+ resultsf = results.format_as_vrs()
+ print(results.format_as_dict())
+ print(resultsf)
+ result = resultsf['ga4gh:VA.x8gKEBS-s638D8uuuQXVJKIgCDcuQJCW']
+ assert result['selected_assembly'] == 'GRCh37'
+ assert result['submitted_variant'] == 'NM_015120.4:c.35T>C'
+ assert result['warnings_and_messages'] == {
+ 'validation_warnings': [],
+ 'lovd_messages': None,
+ 'lovd_corrections': None}
+ assert result['transcript_description'] == \
+ 'Homo sapiens ALMS1 centrosome and basal body associated '\
+ 'protein (ALMS1), transcript variant 1, mRNA'
+ assert result['gene_ids'] == {
+ 'hgnc_id': 'HGNC:428',
+ 'entrez_gene_id': '7840',
+ 'ensembl_gene_id': 'ENSG00000116127',
+ 'ucsc_id': 'uc032nrd.1',
+ 'omim_id': ['606844'],
+ 'ccds_ids': ['CCDS42697'],
+ 'current_symbol': 'ALMS1'}
+ assert result['vrs_transcript_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.mq1Saz29534GzZ6T_jCfUjsqNlifC7pZ',
+ 'label': 'NM_015120.4'
+ },
+ 'start': 145,
+ 'end': 146,
+ 'id': 'ga4gh:SL.x_rdZXUcg7V8Ae5pPVv0OS-faxKyzNQb'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'C'
+ },
+ 'id': 'ga4gh:VA.x8gKEBS-s638D8uuuQXVJKIgCDcuQJCW'
+ }
+ assert result['vrs_refseqgene_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.r47Jw6neAxxIQgeyZLmdPrqFN4S6h7hy',
+ 'label': 'NG_011690.1'},
+ 'start': 5145,
+ 'end': 5146,
+ 'id': 'ga4gh:SL.3CEnQ3JJo2jQbraJ0UHIO-lqBbR4xs15'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'C'},
+ 'id': 'ga4gh:VA.TjhuWhUG-PJapHphK3r0HWyU5b9CgGG_'}
+
+ assert result['vrs_predicted_protein_consequence'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.ggxH8oENu3WDprE6Z_Qg1HvOiT0d8UNa',
+ 'label': 'NP_055935.4'
+ },
+ 'start': 11,
+ 'end': 12,
+ 'id': 'ga4gh:SL.qOpPs9dwgqK6MuH3CTQUDoRYbi9IWwSh'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'P'
+ },
+ 'id': 'ga4gh:VA.cJPulDXE1G73sZS4dHaZX_27-c4pNjYo'
+ }
+ assert result['VRS_mappings_for_primary_assemblies'] == {
+ 'grch37': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.9KdcA9ZpY1Cpvxvg8bMSLYDUpsX6GDLO',
+ 'label': 'NC_000002.11'
+ },
+ 'start': 73613030,
+ 'end': 73613031,
+ 'id': 'ga4gh:SL.IzA6GY6mvDiJ1EzMbe1Cxv6jyhg4u8t7'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CGGA'
+ },
+ 'id': 'ga4gh:VA.rKc_HxeylEKYQzYo_e_kY1lqYLF6Ch5m'
+ },
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.pnAqCRBrTsUoBghSD1yp_jXWSmlbdh4g',
+ 'label': 'NC_000002.12'
+ },
+ 'start': 73385902,
+ 'end': 73385903,
+ 'id': 'ga4gh:SL.ndPBndyntJTTRHuU6tYpgbN6TUsRv94B'
+ },
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'CGGA'
+ },
+ 'id': 'ga4gh:VA.eDiJCZTUpLf1UQHaUFFP1UaPsHfbOjyC'
+ }
+ }
+ assert result['VRS_mappings_for_alt_genomic_loci'] == [{
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.8wk0aF1q6kFjIccOvc8bq3AJVuqWersd',
+ 'label': 'NW_025791766.1'},
+ 'start': 55397,
+ 'end': 55398,
+ 'id': 'ga4gh:SL.vknogVomLiGh3q7IfPcZDAU9Kg6nSvyY'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'C'},
+ 'id': 'ga4gh:VA.pdjaBvFWh8JgChnGQzOTt9s5zxhNudPS'}]
+ def test_format_as_vrs_tx_intronic(self):
+ # from test_variant5
+ variant = 'NC_000023.10:g.33229673A>T'
+ results = self.vv.validate(variant, 'GRCh37', 'all').format_as_vrs()
+ print(results)
+ #'NM_000109.3:c.7+127703T>A'
+ result = results['ga4gh:VA.6LfJDlOTAm1QeCDWfbE0986oJwQ25NSd']
+ assert result['selected_assembly'] == 'GRCh37'
+ assert result['submitted_variant'] == 'NC_000023.10:g.33229673A>T'
+ assert result['warnings_and_messages'] == {
+ 'validation_warnings': [],
+ 'lovd_messages': None, 'lovd_corrections': None,
+ 'vrs_output_warnings': [
+ 'VRSIntronWarning: VRS does not handle mappings shared between '
+ 'genomic and transcript reference sequences. As such only the '
+ 'genomic mappings for this hgvs intronic transcript variant are'
+ ' preserved.']}
+ assert result['gene_ids']['hgnc_id'] == 'HGNC:2928'
+ assert result['gene_ids']['entrez_gene_id'] == '1756'
+ assert result['gene_ids']['ensembl_gene_id'] =='ENSG00000198947'
+ assert result['gene_ids']['ucsc_id'] == 'uc004dda.2'
+ assert result['transcript_description'] == \
+ 'Homo sapiens dystrophin (DMD), transcript variant Dp427c, mRNA'
+ assert result['vrs_intronic_genomic_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.v7noePfnNpK8ghYXEqZ9NukMXW7YeNsm',
+ 'label': 'NC_000023.10'},
+ 'start': 33229672,
+ 'end': 33229673,
+ 'id': 'ga4gh:SL.WqDN0NCy9-Y7PVGwqvI0VtcogTVZCsM4'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.6LfJDlOTAm1QeCDWfbE0986oJwQ25NSd'}
+ assert result['VRS_mappings_for_primary_assemblies'] == {
+ 'grch37': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.v7noePfnNpK8ghYXEqZ9NukMXW7YeNsm',
+ 'label': 'NC_000023.10'},
+ 'start': 33229672,
+ 'end': 33229673,
+ 'id': 'ga4gh:SL.WqDN0NCy9-Y7PVGwqvI0VtcogTVZCsM4'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.6LfJDlOTAm1QeCDWfbE0986oJwQ25NSd'},
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.w0WZEvgJF0zf_P4yyTzjjv9oW1z61HHP',
+ 'label': 'NC_000023.11'},
+ 'start': 33211555,
+ 'end': 33211556,
+ 'id': 'ga4gh:SL.wdORKcdcBA4pikHhob-AdKP-T-IlgKbV'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.I70ooeAvjS4QEfhx9ZYZI3ews44FjqiH'}}
+ def test_format_as_vrs_genomic(self):
+ # from test_variant16
+ variant = 'NC_000017.10:g.48279242G>T'
+ results = self.vv.validate(variant, 'GRCh37', 'all').format_as_vrs()
+ print(results)
+ result = results['ga4gh:VA.Mu21lzIQgKp1Bzq0-E-DiVNNVS1GeF9l']
+ assert result['selected_assembly'] == 'GRCh37'
+ assert result['submitted_variant'] == 'NC_000017.10:g.48279242G>T'
+ assert result['warnings_and_messages'] == {
+ 'validation_warnings': [
+ 'TranscriptIdentificationWarning: No individual transcripts '
+ 'have been identified that fully overlap the described '
+ 'variation in the genomic sequence. Large variants might span '
+ 'one or more genes and are currently only described at the '
+ 'genome (g.) level.'],
+ 'lovd_messages': None,
+ 'lovd_corrections': None}
+ assert result['gene_ids'] == {
+ 'hgnc_id': 'HGNC:2197',
+ 'entrez_gene_id': '1277',
+ 'ensembl_gene_id': 'ENSG00000108821',
+ 'ucsc_id': 'uc002iqm.4',
+ 'omim_id': ['120150'],
+ 'ccds_ids': ['CCDS11561'],
+ 'current_symbol': 'COL1A1'}
+ assert result['transcript_description'] == ''
+ assert result['vrs_intergenic_genomic_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.AjWXsI7AkTK35XW9pgd3UbjpC3MAevlz',
+ 'label': 'NC_000017.10'},
+ 'start': 48279241,
+ 'end': 48279242,
+ 'id': 'ga4gh:SL.BTi2Q9MAcw-eIZjsascBF4kPH7JFHiwK'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.Mu21lzIQgKp1Bzq0-E-DiVNNVS1GeF9l'}
+
+ assert result['vrs_refseqgene_variant'] == {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.QHD1sq0MO0ekfVC3dyTMD76jTTuJdHzC',
+ 'label': 'NG_007400.1'},
+ 'start': 4758,
+ 'end': 4759,
+ 'id': 'ga4gh:SL.Zo3m-4l8X4ec1R9WJoFNnRZJpsf1vxD-'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'A'},
+ 'id': 'ga4gh:VA.T6RVcuFrzTqxCS16D3stnBB8uaf86LoT'}
+ assert result['VRS_mappings_for_primary_assemblies'] == {
+ 'grch37': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.AjWXsI7AkTK35XW9pgd3UbjpC3MAevlz',
+ 'label': 'NC_000017.10'},
+ 'start': 48279241,
+ 'end': 48279242,
+ 'id': 'ga4gh:SL.BTi2Q9MAcw-eIZjsascBF4kPH7JFHiwK'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.Mu21lzIQgKp1Bzq0-E-DiVNNVS1GeF9l'},
+ 'grch38': {
+ 'type': 'Allele',
+ 'location': {
+ 'type': 'SequenceLocation',
+ 'sequenceReference': {
+ 'type': 'SequenceReference',
+ 'refgetAccession': 'SQ.dLZ15tNO1Ur0IcGjwc3Sdi_0A6Yf4zm7',
+ 'label': 'NC_000017.11'},
+ 'start': 50201880,
+ 'end': 50201881,
+ 'id': 'ga4gh:SL.4sKmwc3lN8mn6DxNKFmV_piFt6zW4nkD'},
+ 'state': {
+ 'type': 'LiteralSequenceExpression',
+ 'sequence': 'T'},
+ 'id': 'ga4gh:VA.VMA149-dG25GHnAHd76yLApQ_5lMS2-4'}}
+
+ def test_format_as_vrs_err_result(self):
+ variant = 'NM_007075.3:r.235_236insGCCCACCCACCTGCCAG'
+ results = self.vv.validate(variant, 'GRCh37', 'all').format_as_vrs()
+ assert 'error_1' in results
+ assert results['error_1'] == {
+ 'selected_assembly': 'GRCh37',
+ 'submitted_variant': 'NM_007075.3:r.235_236insGCCCACCCACCTGCCAG',
+ 'warnings_and_messages': {
+ 'validation_warnings': [
+ 'The IUPAC RNA alphabet dictates that RNA variants must '
+ 'use the character u in place of t'],
+ 'lovd_messages': None,
+ 'lovd_corrections': None},
+ 'gene_ids': {},
+ 'transcript_description': ''}
+
+ def test_format_as_vrs_multi_err_result(self):
+ variants = ['NM_007075.3:r.235_236insGCCCACCCACCTGCCAG',
+ 'NC_000017.10:g.41232400_41236235del383']
+ variants = json.dumps(variants)
+ results = self.vv.validate(variants, 'GRCh37', 'all').format_as_vrs()
+ assert 'error_1' in results
+ assert 'error_2' in results
+ assert results['error_1'] == {
+ 'selected_assembly': 'GRCh37',
+ 'submitted_variant': 'NM_007075.3:r.235_236insGCCCACCCACCTGCCAG',
+ 'warnings_and_messages': {
+ 'validation_warnings':
+ ['The IUPAC RNA alphabet dictates that RNA variants must '
+ 'use the character u in place of t'],
+ 'lovd_messages': None,
+ 'lovd_corrections': None},
+ 'gene_ids': {},
+ 'transcript_description': ''}
+ assert results['error_2'] == {
+ 'selected_assembly': 'GRCh37',
+ 'submitted_variant': 'NC_000017.10:g.41232400_41236235del383',
+ 'warnings_and_messages': {
+ 'validation_warnings': [
+ 'Length implied by coordinates must equal sequence deletion length',
+ 'Trailing digits are not permitted in HGVS variant descriptions',
+ 'Refer to http://varnomen.hgvs.org/recommendations/DNA/variant/'],
+ 'lovd_messages': None,
+ 'lovd_corrections': None},
+ 'gene_ids': {},
+ 'transcript_description': ''}