Skip to content

Commit

Permalink
Mark priorities, handle dcterms alternatives used in SBOL3 documents
Browse files Browse the repository at this point in the history
Round-trip tests are not yet working
  • Loading branch information
jakebeal committed Oct 7, 2023
1 parent 8cdba6f commit 1e307da
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 9 deletions.
72 changes: 68 additions & 4 deletions sbol_utilities/sbol3_sbol2_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,21 @@ def _convert_extension_properties(obj3: sbol3.Identified, obj2: sbol2.Identified
extension_properties = (p for p in obj3.properties
if not any(p.startswith(prefix) for prefix in NON_EXTENSION_PROPERTY_PREFIXES))
for p in extension_properties:
obj2.properties[p] = obj3._properties[p] # Can't use setPropertyValue because it may not be a string
obj2.properties[p] = obj3._properties[p].copy() # Can't use setPropertyValue because it may not be a string

@staticmethod
def _value_or_property(obj3: sbol3.Identified, value, property: str):
if property in obj3._properties and len(obj3._properties[property]) == 1:
return value or obj3._properties[property][0]
return value

def _convert_identified(self, obj3: sbol3.Identified, obj2: sbol2.Identified):
"""Map over the other properties of an identified object"""
self._convert_extension_properties(obj3, obj2)
# Map over equivalent properties
obj2.displayId = obj3.display_id
obj2.name = obj3.name
obj2.description = obj3.description
obj2.name = self._value_or_property(obj3, obj3.name, 'http://purl.org/dc/terms/title')
obj2.description = self._value_or_property(obj3, obj3.description, 'http://purl.org/dc/terms/description')
obj2.wasDerivedFrom = obj3.derived_from
obj2.wasGeneratedBy = obj3.generated_by
# Turn measures into extension properties
Expand All @@ -77,7 +83,8 @@ def _convert_toplevel(self, obj3: sbol3.TopLevel, obj2: sbol2.TopLevel):

@staticmethod
def _sbol2_version(obj: sbol3.Identified):
obj.sbol2_version = sbol3.TextProperty(obj, BACKPORT2_VERSION, 0, 1)
if not hasattr(obj, 'sbol2_version'):
obj.sbol2_version = sbol3.TextProperty(obj, BACKPORT2_VERSION, 0, 1)
# TODO: since version is optional, if it's missing, should this be returning '1' or None?
return obj.sbol2_version or '1'

Expand Down Expand Up @@ -105,21 +112,27 @@ def visit_activity(self, act3: sbol3.Activity):
self._convert_toplevel(act3, act2)

def visit_agent(self, a: sbol3.Agent):
# Priority: 3
raise NotImplementedError('Conversion of Agent from SBOL3 to SBOL2 not yet implemented')

def visit_association(self, a: sbol3.Association):
# Priority: 3
raise NotImplementedError('Conversion of Association from SBOL3 to SBOL2 not yet implemented')

def visit_attachment(self, a: sbol3.Attachment):
# Priority: 2
raise NotImplementedError('Conversion of Attachment from SBOL3 to SBOL2 not yet implemented')

def visit_binary_prefix(self, a: sbol3.BinaryPrefix):
# Priority: 4
raise NotImplementedError('Conversion of BinaryPrefix from SBOL3 to SBOL2 not yet implemented')

def visit_collection(self, a: sbol3.Collection):
# Priority: 1
raise NotImplementedError('Conversion of Collection from SBOL3 to SBOL2 not yet implemented')

def visit_combinatorial_derivation(self, a: sbol3.CombinatorialDerivation):
# Priority: 2
raise NotImplementedError('Conversion of CombinatorialDerivation from SBOL3 to SBOL2 not yet implemented')

def visit_component(self, cp3: sbol3.Component):
Expand Down Expand Up @@ -150,61 +163,79 @@ def visit_component(self, cp3: sbol3.Component):
self._convert_toplevel(cp3, cp2)

def visit_component_reference(self, a: sbol3.ComponentReference):
# Priority: 3
raise NotImplementedError('Conversion of ComponentReference from SBOL3 to SBOL2 not yet implemented')

def visit_constraint(self, a: sbol3.Constraint):
# Priority: 2
raise NotImplementedError('Conversion of Constraint from SBOL3 to SBOL2 not yet implemented')

def visit_cut(self, a: sbol3.Cut):
# Priority: 2
raise NotImplementedError('Conversion of Cut from SBOL3 to SBOL2 not yet implemented')

def visit_document(self, doc3: sbol3.Document):
for obj in doc3.objects:
obj.accept(self)

def visit_entire_sequence(self, a: sbol3.EntireSequence):
# Priority: 3
raise NotImplementedError('Conversion of EntireSequence from SBOL3 to SBOL2 not yet implemented')

def visit_experiment(self, a: sbol3.Experiment):
# Priority: 3
raise NotImplementedError('Conversion of Experiment from SBOL3 to SBOL2 not yet implemented')

def visit_experimental_data(self, a: sbol3.ExperimentalData):
# Priority: 3
raise NotImplementedError('Conversion of ExperimentalData from SBOL3 to SBOL2 not yet implemented')

def visit_externally_defined(self, a: sbol3.ExternallyDefined):
# Priority: 3
raise NotImplementedError('Conversion of ExternallyDefined from SBOL3 to SBOL2 not yet implemented')

def visit_implementation(self, a: sbol3.Implementation):
# Priority: 1
raise NotImplementedError('Conversion of Implementation from SBOL3 to SBOL2 not yet implemented')

def visit_interaction(self, a: sbol3.Interaction):
# Priority: 2
raise NotImplementedError('Conversion of Interaction from SBOL3 to SBOL2 not yet implemented')

def visit_interface(self, a: sbol3.Interface):
# Priority: 3
raise NotImplementedError('Conversion of Interface from SBOL3 to SBOL2 not yet implemented')

def visit_local_sub_component(self, a: sbol3.LocalSubComponent):
# Priority: 2
raise NotImplementedError('Conversion of LocalSubComponent from SBOL3 to SBOL2 not yet implemented')

def visit_measure(self, a: sbol3.Measure):
# Priority: 3
raise NotImplementedError('Conversion of Measure from SBOL3 to SBOL2 not yet implemented')

def visit_model(self, a: sbol3.Model):
# Priority: 3
raise NotImplementedError('Conversion of Model from SBOL3 to SBOL2 not yet implemented')

def visit_participation(self, a: sbol3.Participation):
# Priority: 2
raise NotImplementedError('Conversion of Participation from SBOL3 to SBOL2 not yet implemented')

def visit_plan(self, a: sbol3.Plan):
# Priority: 3
raise NotImplementedError('Conversion of Plan from SBOL3 to SBOL2 not yet implemented')

def visit_prefixed_unit(self, a: sbol3.PrefixedUnit):
# Priority: 4
raise NotImplementedError('Conversion of PrefixedUnit from SBOL3 to SBOL2 not yet implemented')

def visit_range(self, a: sbol3.Range):
# Priority: 2
raise NotImplementedError('Conversion of Range from SBOL3 to SBOL2 not yet implemented')

def visit_si_prefix(self, a: sbol3.SIPrefix):
# Priority: 4
raise NotImplementedError('Conversion of SIPrefix from SBOL3 to SBOL2 not yet implemented')

def visit_sequence(self, seq3: sbol3.Sequence):
Expand All @@ -220,27 +251,35 @@ def visit_sequence(self, seq3: sbol3.Sequence):
self._convert_toplevel(seq3, seq2)

def visit_sequence_feature(self, a: sbol3.SequenceFeature):
# Priority: 1
raise NotImplementedError('Conversion of SequenceFeature from SBOL3 to SBOL2 not yet implemented')

def visit_singular_unit(self, a: sbol3.SingularUnit):
# Priority: 4
raise NotImplementedError('Conversion of SingularUnit from SBOL3 to SBOL2 not yet implemented')

def visit_sub_component(self, a: sbol3.SubComponent):
# Priority: 1
raise NotImplementedError('Conversion of SubComponent from SBOL3 to SBOL2 not yet implemented')

def visit_unit_division(self, a: sbol3.UnitDivision):
# Priority: 4
raise NotImplementedError('Conversion of UnitDivision from SBOL3 to SBOL2 not yet implemented')

def visit_unit_exponentiation(self, a: sbol3.UnitExponentiation):
# Priority: 4
raise NotImplementedError('Conversion of UnitExponentiation from SBOL3 to SBOL2 not yet implemented')

def visit_unit_multiplication(self, a: sbol3.UnitMultiplication):
# Priority: 4
raise NotImplementedError('Conversion of UnitMultiplication from SBOL3 to SBOL2 not yet implemented')

def visit_usage(self, a: sbol3.Usage):
# Priority: 3
raise NotImplementedError('Conversion of Usage from SBOL3 to SBOL2 not yet implemented')

def visit_variable_feature(self, a: sbol3.VariableFeature):
# Priority: 2
raise NotImplementedError('Conversion of VariableFeature from SBOL3 to SBOL2 not yet implemented')


Expand Down Expand Up @@ -321,18 +360,23 @@ def visit_activity(self, act2: sbol2.Activity):
self._convert_toplevel(act2, act3)

def visit_agent(self, a: sbol2.Agent):
# Priority: 3
raise NotImplementedError('Conversion of Agent from SBOL2 to SBOL3 not yet implemented')

def visit_association(self, a: sbol2.Association):
# Priority: 3
raise NotImplementedError('Conversion of Association from SBOL2 to SBOL3 not yet implemented')

def visit_attachment(self, a: sbol2.Attachment):
# Priority: 2
raise NotImplementedError('Conversion of Attachment from SBOL2 to SBOL3 not yet implemented')

def visit_collection(self, a: sbol2.Collection):
# Priority: 1
raise NotImplementedError('Conversion of Collection from SBOL2 to SBOL3 not yet implemented')

def visit_combinatorial_derivation(self, a: sbol2.CombinatorialDerivation):
# Priority: 2
raise NotImplementedError('Conversion of CombinatorialDerivation from SBOL2 to SBOL3 not yet implemented')

def visit_component_definition(self, cd2: sbol2.ComponentDefinition):
Expand Down Expand Up @@ -363,9 +407,11 @@ def visit_component_definition(self, cd2: sbol2.ComponentDefinition):
self._convert_toplevel(cd2, cp3)

def visit_component(self, a: sbol2.Component):
# Priority: 2
raise NotImplementedError('Conversion of Component from SBOL2 to SBOL3 not yet implemented')

def visit_cut(self, a: sbol2.Cut):
# Priority: 2
raise NotImplementedError('Conversion of Cut from SBOL2 to SBOL3 not yet implemented')

def visit_document(self, doc2: sbol2.Document):
Expand Down Expand Up @@ -399,45 +445,59 @@ def visit_document(self, doc2: sbol2.Document):
# designs, builds, tests, analyses, sampleRosters, citations, keywords

def visit_experiment(self, a: sbol2.Experiment):
# Priority: 3
raise NotImplementedError('Conversion of Experiment from SBOL2 to SBOL3 not yet implemented')

def visit_experimental_data(self, a: sbol2.ExperimentalData):
# Priority: 3
raise NotImplementedError('Conversion of ExperimentalData from SBOL2 to SBOL3 not yet implemented')

def visit_functional_component(self, a: sbol2.FunctionalComponent):
# Priority: 3
raise NotImplementedError('Conversion of FunctionalComponent from SBOL2 to SBOL3 not yet implemented')

def visit_generic_location(self, a: sbol2.GenericLocation):
# Priority: 3
raise NotImplementedError('Conversion of GenericLocation from SBOL2 to SBOL3 not yet implemented')

def visit_implementation(self, a: sbol2.Implementation):
# Priority: 1
raise NotImplementedError('Conversion of Implementation from SBOL2 to SBOL3 not yet implemented')

def visit_interaction(self, a: sbol2.Interaction):
# Priority: 2
raise NotImplementedError('Conversion of Interaction from SBOL2 to SBOL3 not yet implemented')

def visit_maps_to(self, a: sbol2.mapsto.MapsTo):
# Priority: 3
raise NotImplementedError('Conversion of MapsTo from SBOL2 to SBOL3 not yet implemented')

def visit_measure(self, a: sbol2.measurement.Measurement):
# Priority: 3
raise NotImplementedError('Conversion of Measure from SBOL2 to SBOL3 not yet implemented')

def visit_model(self, a: sbol2.model.Model):
# Priority: 3
raise NotImplementedError('Conversion of Model from SBOL2 to SBOL3 not yet implemented')

def visit_module(self, a: sbol2.Module):
# Priority: 3
raise NotImplementedError('Conversion of Module from SBOL2 to SBOL3 not yet implemented')

def visit_module_definition(self, a: sbol2.ModuleDefinition):
# Priority: 3
raise NotImplementedError('Conversion of ModuleDefinition from SBOL2 to SBOL3 not yet implemented')

def visit_participation(self, a: sbol2.Participation):
# Priority: 2
raise NotImplementedError('Conversion of Participation from SBOL2 to SBOL3 not yet implemented')

def visit_plan(self, a: sbol2.Plan):
# Priority: 3
raise NotImplementedError('Conversion of Plan from SBOL2 to SBOL3 not yet implemented')

def visit_range(self, a: sbol2.Range):
# Priority: 2
raise NotImplementedError('Conversion of Range from SBOL2 to SBOL3 not yet implemented')

def visit_sequence(self, seq2: sbol2.Sequence):
Expand All @@ -454,15 +514,19 @@ def visit_sequence(self, seq2: sbol2.Sequence):
self._convert_toplevel(seq2, seq3)

def visit_sequence_annotation(self, seq2: sbol2.SequenceAnnotation):
# Priority: 1
raise NotImplementedError('Conversion of SequenceAnnotation from SBOL2 to SBOL3 not yet implemented')

def visit_sequence_constraint(self, seq2: sbol2.sequenceconstraint.SequenceConstraint):
# Priority: 2
raise NotImplementedError('Conversion of SequenceConstraint from SBOL2 to SBOL3 not yet implemented')

def visit_usage(self, a: sbol2.Usage):
# Priority: 3
raise NotImplementedError('Conversion of Usage from SBOL2 to SBOL3 not yet implemented')

def visit_variable_component(self, a: sbol2.VariableComponent):
# Priority: 2
raise NotImplementedError('Conversion of VariableComponent from SBOL2 to SBOL3 not yet implemented')


Expand Down
32 changes: 27 additions & 5 deletions test/test_sbol2_sbol3_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,40 @@

class TestDirectSBOL2SBOL3Conversion(unittest.TestCase):

# TODO: turn on validation
def test_3to2_conversion(self):
"""Test ability to convert a simple part from SBOL3 to SBOL2"""
# Load an SBOL3 document and check its contents
doc3 = sbol3.Document()
doc3.read(TEST_FILES / 'BBa_J23101.nt')
# Convert to SBOL2 and check contents
doc2 = convert3to2(doc3, True)
with tempfile.NamedTemporaryFile(suffix='.xml') as tmp:
doc2.write(tmp.name)
self.assertFalse(file_diff(tmp.name, str(TEST_FILES / 'BBa_J23101.xml')))
doc2.read(tmp.name)
doc3_loop = convert3to2()
#self.assertEqual(len(doc2.validate()), 0)
with tempfile.NamedTemporaryFile(suffix='.xml') as tmp2:
doc2.write(tmp2.name)
self.assertFalse(file_diff(tmp2.name, str(TEST_FILES / 'BBa_J23101.xml')))
doc3_loop = convert2to3(doc2)
#self.assertEqual(len(doc3_loop.validate()), 0)
with tempfile.NamedTemporaryFile(suffix='.nt') as tmp3:
doc3_loop.write(tmp3.name)
self.assertFalse(file_diff(tmp3.name, str(TEST_FILES / 'BBa_J23101_patched.nt')))

def test_2to3_conversion(self):
"""Test ability to convert a simple part from SBOL3 to SBOL2"""
# Load an SBOL3 document and check its contents
doc2 = sbol2.Document()
doc2.read(TEST_FILES / 'BBa_J23101.xml')
# Convert to SBOL3 and check contents
doc3 = convert2to3(doc2, True)
#self.assertEqual(len(doc3.validate()), 0)
with tempfile.NamedTemporaryFile(suffix='.nt') as tmp3:
doc3.write(tmp3.name)
self.assertFalse(file_diff(tmp3.name, str(TEST_FILES / 'BBa_J23101.nt')))
doc2_loop = convert3to2(doc3)
# self.assertEqual(len(doc2_loop.validate()), 0)
with tempfile.NamedTemporaryFile(suffix='.xml') as tmp2:
doc2_loop.write(tmp2.name)
self.assertFalse(file_diff(tmp2.name, str(TEST_FILES / 'BBa_J23101.xml')))


if __name__ == '__main__':
Expand Down

0 comments on commit 1e307da

Please sign in to comment.