Skip to content

Commit

Permalink
feat(reverse-strand): Now showing reverse strand transcript in 5'-3' …
Browse files Browse the repository at this point in the history
…orientation relative to the transcript by default (can be configured).
  • Loading branch information
andreasprlic committed Oct 3, 2024
1 parent fed5af4 commit 9a2e049
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 31 deletions.
8 changes: 7 additions & 1 deletion src/hgvs/pretty/console/chrom_seq_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
class ChromSeqRendered(BasicRenderer):

def legend(self) -> str:
return "seq -> : "

if self.orientation < 0 and self.config.reverse_display:
arrow = '<-'
else:
arrow = '->'

return f"seq {arrow} : "

def display(self, data: VariantData) -> str:
"""colors the ref sequences with adenine (A, green), thymine (T, red), cytosine (C, yellow), and guanine (G, blue)"""
Expand Down
7 changes: 6 additions & 1 deletion src/hgvs/pretty/console/chrom_seq_reverse_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
class ChromReverseSeqRendered(BasicRenderer):

def legend(self) -> str:
return "seq <- : "
if self.orientation < 0 and self.config.reverse_display:
arrow = '->'
else:
arrow = '<-'

return f"seq {arrow} : "

def display(self, data: VariantData) -> str:
"""colors the ref sequences with adenine (A, green), thymine (T, red), cytosine (C, yellow), and guanine (G, blue)"""
Expand Down
15 changes: 9 additions & 6 deletions src/hgvs/pretty/console/prot_seq_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
class ProtSeqRenderer(BasicRenderer):

def legend(self):
legend = "aa seq -> : "
if self.orientation < 0:
legend = "aa seq <- : "
return legend

if self.orientation > 0:
arrow = "->"
elif self.orientation < 0 and self.config.reverse_display:
arrow = "->"
else:
arrow = "<-"

return f"aa seq {arrow} : "

def display(self, data: VariantData) -> str:
if not data.var_c_or_n:
Expand All @@ -19,8 +24,6 @@ def display(self, data: VariantData) -> str:
var_str = ""
for pdata in data.position_details:

p = pdata.chromosome_pos

if not pdata.mapped_pos:
var_str += " "
continue
Expand Down
22 changes: 18 additions & 4 deletions src/hgvs/pretty/console/shuffled_variant.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from logging import info
from hgvs.pretty.models import VariantCoords, VariantData
from hgvs.pretty.console.renderer import BasicRenderer
from hgvs.sequencevariant import SequenceVariant
Expand Down Expand Up @@ -61,23 +62,36 @@ def display(self, data: VariantData) -> str:

var_str = ""
in_range = False

reverse_display = False
if self.orientation < 0 and self.config.reverse_display:
reverse_display = True

for pdata in data.position_details:
p = pdata.chromosome_pos

if not p:
if in_range:
var_str += "-"
else:
var_str += " "
continue

if p == start:
if not reverse_display and p == start:
var_str += split_char
in_range = True
elif reverse_display and p == end:
var_str += split_char
in_range = True
elif p == end:
elif not reverse_display and p == end:
var_str += split_char
in_range = False
elif p > end and in_range:
elif reverse_display and p == start:
var_str += split_char
in_range = False
elif not reverse_display and p > end and in_range:
in_range = False
var_str += " "
elif reverse_display and p < start and in_range:
in_range = False
var_str += " "
elif in_range:
Expand Down
7 changes: 5 additions & 2 deletions src/hgvs/pretty/console/tx_alig_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
class TxAligRenderer(BasicRenderer):

def legend(self) -> str:
orientation = "->"
if self.orientation < 0:
if self.orientation > 0:
orientation = "->"
elif self.orientation < 0 and self.config.reverse_display:
orientation = "->"
else:
orientation = "<-"
return f"tx seq {orientation} : "

Expand Down
6 changes: 2 additions & 4 deletions src/hgvs/pretty/console/tx_ref_disagree_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ def display(self, data: VariantData) -> str:
from hgvs.pretty.console.constants import ENDC, TRED

var_str = ""
counter = -1
for p in range(data.display_start + 1, data.display_end + 1):
counter += 1
pdata = data.position_details[counter]

for pdata in data.position_details:
c_offset = pdata.c_offset
if not pdata.mapped_pos:
var_str += " "
Expand Down
6 changes: 5 additions & 1 deletion src/hgvs/pretty/datacompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def _get_prot_alt(
c_pos = int(c_interval.start.base) - 1
c3 = (c_pos) % 3
aa_char = tlc[c3]
if strand < 0:
if strand < 0 and not self.config.reverse_display:
aa_char = tlc[2 - c3]

return ProteinData(c_pos, aa, tlc, aa_char, var_p, is_init_met, is_stop_codon, aa_index)
Expand Down Expand Up @@ -347,6 +347,10 @@ def data(

strand = mapper.strand if mapper else 1

if strand < 0 and self.config.reverse_display:
pd = reversed(position_details)
position_details = list(pd)

vd = VariantData(
seq_start,
seq_end,
Expand Down
1 change: 1 addition & 0 deletions src/hgvs/pretty/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,4 @@ class PrettyConfig:
all: bool = False # print all possible hgvs_c (for all UTA transcripts)
show_reverse_strand: bool = False # show the reverse strand sequence for the chromosome
alt_aln_method:str = 'splign'
reverse_display:bool = True # display reverse strand as the main strand (5'->3')
13 changes: 7 additions & 6 deletions src/hgvs/pretty/pretty_print.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
from hgvs.sequencevariant import SequenceVariant




class PrettyPrint:
"""A class that provides a pretty display of the genomic context of a variant."""

Expand All @@ -37,7 +35,8 @@ def __init__(
infer_hgvs_c=True,
all=False,
show_reverse_strand=False,
alt_aln_method='splign'
alt_aln_method='splign',
reverse_display = True
):
"""
:param hdp: HGVS Data Provider Interface-compliant instance
Expand All @@ -60,7 +59,8 @@ def __init__(
infer_hgvs_c,
all,
show_reverse_strand,
alt_aln_method
alt_aln_method,
reverse_display
)

def _get_assembly_mapper(self) -> AssemblyMapper:
Expand Down Expand Up @@ -213,7 +213,9 @@ def create_repre(

renderer_cls = [ChrPositionInfo, ChrRuler, ChromSeqRendered]

if self.config.show_reverse_strand:
# if we show reverse strand transcripts in forward facing orientation, always
# show both forward and reverse strand sequences.
if self.config.show_reverse_strand or self.config.reverse_display and data.strand < 0:
renderer_cls.append(ChromReverseSeqRendered)

renderer_cls.append(TxRefDisagreeRenderer)
Expand Down Expand Up @@ -249,7 +251,6 @@ def create_repre(
self.config, data.strand, var_g, fully_justified_var
)
fully_justified_str = fully_justified_renderer.display(data)

if self.config.showAllShuffleableRegions:
var_str += shuffled_seq_header + left_shuffled_str + "\n"
var_str += shuffled_seq_header + right_shuffled_str + "\n"
Expand Down
70 changes: 64 additions & 6 deletions tests/test_pretty_print.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def setUpClass(cls):
cls.hdp = hgvs.dataproviders.uta.connect(mode=None, cache=None)
cls.pp = PrettyPrint(
cls.hdp,
reverse_display=False
)
cls.pp.useColor = False

Expand Down Expand Up @@ -87,11 +88,39 @@ def test_var_c1_reverse(self):
for r, e in zip(result, expected_str):
self.assertEqual(e, r)

def test_var_c1_reverse_flipped_display(self):
""" test the reversed display on <- strand"""
hgvs_c = "NM_001177507.2:c.1="
var_c = self.hp.parse(hgvs_c)

pp = PrettyPrint(self.hdp, show_reverse_strand=True, reverse_display=True)
result = pp.display(var_c)
print(result)
result = result.split("\n")
expected_str = (
"hgvs_g : NC_000007.13:g.36763753=\n" +
"hgvs_c : NM_001177507.2:c.1=\n" +
"hgvs_p : NP_001170978.1:p.Met1?\n" +
" : 36,763,770 36,763,750\n" +
"chrom pos : | . | . | . | . \n" +
"seq <- : AACCCCACGTATCGAGCCTCTACGTCAGGGGGACCTTTTAG\n" +
"seq -> : TTGGGGTGCATAGCTCGGAGATGCAGTCCCCCTGGAAAATC\n" +
"region : = \n" +
"tx seq -> : ttggggtgcatagctcggagATGCAGTCCCCCTGGAAAATC\n" +
"tx pos : | . | . | . | . | \n" +
" : -20 -10 1 10 20\n" +
"aa seq -> : MetGlnSerProTrpLysIle\n" +
"aa pos : ... \n" +
" : 1 \n"
).split("\n")
for r, e in zip(result, expected_str):
self.assertEqual(e, r)

def test_var_g_substitution(self):
hgvs_g = "NC_000007.13:g.36561662C>T"
var_g = self.hp.parse(hgvs_g)

pp = PrettyPrint(self.hdp, show_reverse_strand=True)
pp = PrettyPrint(self.hdp, show_reverse_strand=True, reverse_display=False)

result = pp.display(var_g)
print(result)
Expand Down Expand Up @@ -132,6 +161,35 @@ def test_var_g_ins(self):
for r, e in zip(result, expected_str):
self.assertEqual(e, r)

def test_atta_forward(self):
""" the ATTA[2]>ATTA[3] variant now displayed forward facing:"""

hgvs_g = "NC_000005.10:g.123346517_123346518insATTA"
var_g = self.hp.parse(hgvs_g)
pp = PrettyPrint(self.hdp, show_reverse_strand=True, reverse_display=True)
result = pp.display(var_g)
print(result)
result = result.split("\n")
expected_str = (
"hgvs_g : NC_000005.10:g.123346517_123346518insATTA\n" +
"hgvs_c : NM_001166226.1:c.*1_*2insTAAT\n" +
"hgvs_p : NP_001159698.1:p.?\n" +
" : 123,346,540 123,346,520 123,346,500\n" +
"chrom pos : . | . | . | . | . | \n" +
"seq <- : AACCGTTTTTCGTTACGGTCATTAATTATTGTAAACCTTTTCGAAATA\n" +
"seq -> : TTGGCAAAAAGCAATGCCAGTAATTAATAACATTTGGAAAAGCTTTAT\n" +
"region : |------| \n" +
"tx seq -> : TTGGCAAAAAGCAATGCCAGTAATTAATAACATTTGGAAAAGCTTTAT\n" +
"tx pos : | . | . | | . | . | \n" +
" : 2860 2870 2880 *10 *20\n" +
"aa seq -> : LeuAlaLysSerAsnAlaSerAsnTer \n" +
"aa pos : ... ||| \n" +
" : 960 \n" +
"ref>alt : ATTA[2]>ATTA[3]\n"
).split("\n")
for r, e in zip(result, expected_str):
self.assertEqual(e, r)

def test_var_g_dup(self):
hgvs_g = "NC_000005.10:g.123346522_123346525dup"
var_g = self.hp.parse(hgvs_g)
Expand Down Expand Up @@ -366,7 +424,7 @@ def test_tiny(self):
hgvs_g = "NC_000005.10:g.123346517_123346518insATTA"
var_g = self.hp.parse(hgvs_g)

tiny_pp = PrettyPrint(self.hdp, padding_left=0, padding_right=0)
tiny_pp = PrettyPrint(self.hdp, padding_left=0, padding_right=0, reverse_display=False)

result = tiny_pp.display(var_g)
print(result)
Expand Down Expand Up @@ -408,7 +466,7 @@ def test_hgvs_c(self):
hgvs_c = "NM_004572.3:c.-9_12dup"
var_c = self.hp.parse(hgvs_c)
pp = PrettyPrint(
self.hdp, padding_left=10, padding_right=110, useColor=False, showLegend=False
self.hdp, padding_left=10, padding_right=110, useColor=False, showLegend=False, reverse_display=False
)
result = pp.display(var_c)

Expand Down Expand Up @@ -508,7 +566,7 @@ def test_ref_disagree_del(self):
hgvs_c = "NM_000682.6:c.901_911del" # a del variant

var_c = self.hp.parse(hgvs_c)
pp = PrettyPrint(self.hdp, infer_hgvs_c=True, padding_left=30, padding_right=40)
pp = PrettyPrint(self.hdp, infer_hgvs_c=True, padding_left=30, padding_right=40, reverse_display=False)
result = pp.display(var_c)
print(result)
result = result.split("\n")
Expand All @@ -520,7 +578,7 @@ def test_ref_disagree_del(self):
+ " : 96,780,960 96,780,980 96,781,000 96,781,020\n"
+ "chrom pos : | . | . | . | . _________ | . | . | . | . \n"
+ "seq -> : TGCCTGGGGTTCACACTCTTCCTCCTCCTCCTCCTCCTCTTC.........GGCTTCATCCTCTGGAGATGCCCCACAAACACCCTCCTTC\n"
+ "tx ref dif: DDDDDDDDD \n"
+ "tx ref dif: DDDDDDDDD \n"
+ "region : x----------x \n"
+ "tx seq <- : ACGGACCCCAAGTGTGAGAAGGAGGAGGAGGAGGAGGAGAAGGAGGAGAAGTCGAAGTAGGAGACCTCTACGGGGTGTTTGTGGGAGGAAG\n"
+ "tx pos : | . | . | . | . | . | . | . | . | . \n"
Expand All @@ -547,7 +605,7 @@ def test_ruler(self):
"""Test the ruler display option turned on."""
hgvs_c = "NM_001111.4:c.298G>A"
var_c = self.hp.parse(hgvs_c)
pp = PrettyPrint(self.hdp, showLegend=False)
pp = PrettyPrint(self.hdp, showLegend=False, reverse_display=False)

result = pp.display(var_c)

Expand Down

0 comments on commit 9a2e049

Please sign in to comment.