Skip to content

Commit

Permalink
Implement Extension from_json, and fix bug with default color from json
Browse files Browse the repository at this point in the history
  • Loading branch information
UnHumbleBen committed May 14, 2022
1 parent 9b9e401 commit 819d0d8
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 deletions.
48 changes: 39 additions & 9 deletions scadnano/scadnano.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,11 @@ def m13(rotation: int = 5587, variant: M13Variant = M13Variant.p7249) -> str:
# Loopout keys
loopout_key = 'loopout'

# Extension keys
extension_key = 'extension'
display_length_key = 'display_length'
display_angle_key = 'display_angle'

# Modification keys
mod_location_key = 'location'
mod_display_text_key = 'display_text'
Expand Down Expand Up @@ -2072,6 +2077,26 @@ def set_name(self, name: str) -> None:
"""Sets name of this :any:`Extension`."""
self.name = name

@staticmethod
def from_json(json_map: Dict[str, Any]) -> 'Extension':
def float_transformer(x): return float(x)
num_bases_str = mandatory_field(Extension, json_map, extension_key)
num_bases = int(num_bases_str)
display_length = optional_field(_default_extension.display_length,
json_map, display_length_key, transformer=float_transformer)
display_angle = optional_field(_default_extension.display_angle,
json_map, display_angle_key, transformer=float_transformer)
name = json_map.get(domain_name_key)
label = json_map.get(domain_label_key)
return Extension(
num_bases=num_bases,
display_length=display_length,
display_angle=display_angle,
label=label, name=name)


# Default Extension object to allow for access to default extension values. num_bases is a dummy.
_default_extension: Extension = Extension(num_bases=5)

_wctable = str.maketrans('ACGTacgt', 'TGCAtgca')

Expand Down Expand Up @@ -2867,10 +2892,12 @@ def from_json(json_map: dict) -> 'Strand': # remove quotes when Py3.6 support d
if len(domain_jsons) == 0:
raise IllegalDesignError(f'{domains_key} list cannot be empty')

domains: List[Union[Domain, Loopout]] = []
domains: List[Union[Domain, Loopout, Extension]] = []
for domain_json in domain_jsons:
if loopout_key in domain_json:
domains.append(Loopout.from_json(domain_json))
elif extension_key in domain_json:
domains.append(Extension.from_json(domain_json))
else:
domains.append(Domain.from_json(domain_json))
if isinstance(domains[0], Loopout):
Expand All @@ -2883,14 +2910,17 @@ def from_json(json_map: dict) -> 'Strand': # remove quotes when Py3.6 support d

dna_sequence = optional_field(None, json_map, dna_sequence_key, legacy_keys=legacy_dna_sequence_keys)

color_str = json_map.get(color_key,
default_scaffold_color if is_scaffold else default_strand_color)

This comment has been minimized.

Copy link
@UnHumbleBen

UnHumbleBen May 14, 2022

Author Collaborator

Edit: Click on the commit link to see which line I commented on.

@dave-doty I notice a bug here where the color_str will have type Color instead of str in the case where color_key is not stored in the json_map. This type issue leads to a problem in the line below:

color = Color(hex_string=color_str)

I fixed the problem in this commit by removing the use of the optional parameter in Dict.get and assigning the color variable here:

if color_str is None:
color = default_scaffold_color if is_scaffold else default_strand_color
else:

Just thought I would let you know. This was discovered by a test case I wrote that does not include a color field in the strand object:

"strands": [
{
"domains": [
{"helix": 0, "forward": true, "start": 0, "end": 10},
{"extension": 5, "display_length": 1.4, "display_angle": 50.0}
],
"is_scaffold": true
}
]

if isinstance(color_str, int):
def decimal_int_to_hex(d: int) -> str:
return "#" + "{0:#08x}".format(d, 8)[2:] # type: ignore
color_str = json_map.get(color_key)

if color_str is None:
color = default_scaffold_color if is_scaffold else default_strand_color
else:
if isinstance(color_str, int):
def decimal_int_to_hex(d: int) -> str:
return "#" + "{0:#08x}".format(d, 8)[2:] # type: ignore

color_str = decimal_int_to_hex(color_str)
color = Color(hex_string=color_str)
color_str = decimal_int_to_hex(color_str)
color = Color(hex_string=color_str)

label = json_map.get(strand_label_key)

Expand Down Expand Up @@ -5798,7 +5828,7 @@ def _check_types(self) -> None:
for strand in self.strands:
_check_type(strand, Strand)
for substrand in strand.domains:
_check_type_is_one_of(substrand, [Domain, Loopout])
_check_type_is_one_of(substrand, [Domain, Loopout, Extension])
if isinstance(substrand, Domain):
_check_type(substrand.helix, int)
_check_type(substrand.forward, bool)
Expand Down
4 changes: 2 additions & 2 deletions tests/scadnano_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4857,15 +4857,15 @@ def test_from_json_extension_design(self) -> None:
{
"domains": [
{"helix": 0, "forward": true, "start": 0, "end": 10},
{"extension": 5, "relative_offset": [1.4, -0.3]}
{"extension": 5, "display_length": 1.4, "display_angle": 50.0}
],
"is_scaffold": true
}
]
}
"""
design = sc.Design.from_scadnano_json_str(json_str)
self.assertEqual(sc.Extension(5, (1.4, -0.3)), design.strands[0].domains[1])
self.assertEqual(sc.Extension(5, display_length=1.4, display_angle=50.0), design.strands[0].domains[1])

def test_to_json_extension_design__extension(self) -> None:
# Setup
Expand Down

0 comments on commit 819d0d8

Please sign in to comment.