diff --git a/sbol_utilities/conversion.py b/sbol_utilities/conversion.py index 6510bdb4..225c55b7 100644 --- a/sbol_utilities/conversion.py +++ b/sbol_utilities/conversion.py @@ -80,7 +80,7 @@ def convert2to3(sbol2_doc: Union[str, sbol2.Document], namespaces=None, use_nati :return: equivalent SBOL3 document """ if use_native_converter: - return sbol_utilities.sbol3_sbol2_conversion.convert2to3(sbol2_doc) + return sbol_utilities.sbol3_sbol2_conversion.convert2to3(sbol2_doc, namespaces) # if we've started with a Document in memory, write it to a temp file if namespaces is None: diff --git a/sbol_utilities/sbol3_sbol2_conversion.py b/sbol_utilities/sbol3_sbol2_conversion.py index 55f76c8c..ca3f6bee 100644 --- a/sbol_utilities/sbol3_sbol2_conversion.py +++ b/sbol_utilities/sbol3_sbol2_conversion.py @@ -1,5 +1,6 @@ import sbol3 import sbol2 +from sbol2 import mapsto, model, sequenceconstraint # Namespaces from rdflib import URIRef @@ -53,21 +54,26 @@ 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 if obj3.measures: raise NotImplementedError('Conversion of measures from SBOL3 to SBOL2 not yet implemented') - pass def _convert_toplevel(self, obj3: sbol3.TopLevel, obj2: sbol2.TopLevel): """Map over the other properties of a TopLevel object""" @@ -77,12 +83,15 @@ 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' def visit_activity(self, act3: sbol3.Activity): # Make the Activity object and add it to the document act2 = sbol2.Activity(act3.identity, version=self._sbol2_version(act3)) + self.doc2.activities.add(act2) if act3.types: if len(act3.types) > 1: raise NotImplementedError('Conversion of multi-type Activities to SBOL2 not yet implemented:' @@ -99,26 +108,31 @@ def visit_activity(self, act3: sbol3.Activity): act2.associations = [assoc.accept(self) for assoc in act3.association] # TODO: pySBOL3 is currently missing wasInformedBy (https://github.com/SynBioDex/pySBOL3/issues/436 # act2.wasInformedBy = act3.informed_by - self.doc2.activities.add(act2) # Map over all other TopLevel properties and extensions not covered by the constructor 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): @@ -149,12 +163,15 @@ 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): @@ -162,48 +179,63 @@ def visit_document(self, doc3: sbol3.Document): 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): @@ -219,30 +251,285 @@ 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') +class SBOL2To3ConversionVisitor: + """This class is used to map every object in an SBOL3 document into an empty SBOL2 document""" + + doc3: sbol3.Document + namespaces: list + + def __init__(self, doc2: sbol2.Document, namespaces: list): + # Create the target document + self.doc3 = sbol3.Document() + self.namespaces = namespaces + # # Immediately run the conversion + self._convert(doc2) + + def _convert(self, doc2: sbol2.Document): + # Note: namespaces don't need to be bound for SBOL3 documents, which don't usually use XML + # We can skip all the preliminaries and just go to conversion + self.visit_document(doc2) + # TODO: check if there is additional work needed for Annotation & GenericTopLevel conversion + + @staticmethod + def _convert_extension_properties(obj2: sbol2.Identified, obj3: sbol3.Identified): + """Copy over extension properties""" + extension_properties = (p for p in obj2.properties + if not any(p.startswith(prefix) for prefix in NON_EXTENSION_PROPERTY_PREFIXES)) + for p in extension_properties: + obj3._properties[p] = obj2.properties[p] + + def _convert_identified(self, obj2: sbol2.Identified, obj3: sbol3.Identified): + """Map over the other properties of an Identified object""" + self._convert_extension_properties(obj2, obj3) + # Map over equivalent properties + # display_id and namespace are handled during creation + if obj2.version: # Save version for unpacking later if needed + obj3.sbol2_version = sbol3.TextProperty(obj3, BACKPORT2_VERSION, 0, 1) + obj3.sbol2_version = obj2.version + obj3.name = obj2.name + obj3.description = obj2.description + obj3.derived_from = obj2.wasDerivedFrom + obj3.generated_by = obj2.wasGeneratedBy + # TODO: unpack measures from extension properties + + def _convert_toplevel(self, obj2: sbol2.TopLevel, obj3: sbol3.TopLevel): + """Map over the other properties of a TopLevel object""" + self._convert_identified(obj2, obj3) + obj3.attachments = [a.identity for a in obj2.attachments] + + def _sbol3_namespace(self, obj2: sbol2.TopLevel): + # If a namespace is explicitly set, that takes priority + if BACKPORT3_NAMESPACE in obj2.properties: + namespaces = obj2.properties[BACKPORT3_NAMESPACE] + if len(namespaces) != 1: + raise ValueError(f'Object {obj2.identity} backport namespace property should have precisely one value, ' + f'but was {namespaces}') + return namespaces[0] + # Check if the object starts with any of the provided namespaces + for namespace in self.namespaces: + if obj2.identity.startswith(namespace): + return namespace + # Otherwise, use default behavior + return None + + def visit_activity(self, act2: sbol2.Activity): + # Make the Activity object and add it to the document + act3 = sbol3.Activity(act2.identity, namespace=self._sbol3_namespace(act2), + start_time=act2.startedAtTime, end_time=act2.endedAtTime) + self.doc3.add(act3) + # Convert child objects after adding to document + if act2.types: # TODO: wrapping not needed after resolution of https://github.com/SynBioDex/pySBOL2/issues/428 + act3.types = [act2.types] + act3.usage = [usage.visit_usage(self) for usage in act2.usages] + act3.association = [assoc.visit_association(self) for assoc in act2.associations] + # TODO: pySBOL3 is currently missing wasInformedBy (https://github.com/SynBioDex/pySBOL3/issues/436 + # act3.informed_by = act2.wasInformedBy + # Map over all other TopLevel properties and extensions not covered by the constructor + 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): + # Remap type if it's one of the ones that needs remapping; otherwise pass through unchanged + type_map = {sbol2.BIOPAX_DNA: sbol3.SBO_DNA, + 'http://www.biopax.org/release/biopax-level3.owl#Dna': sbol3.SBO_DNA, # TODO: make reversible + sbol2.BIOPAX_RNA: sbol3.SBO_RNA, + 'http://www.biopax.org/release/biopax-level3.owl#Rna': sbol3.SBO_RNA, # TODO: make reversible + sbol2.BIOPAX_PROTEIN: sbol3.SBO_PROTEIN, + sbol2.BIOPAX_SMALL_MOLECULE: sbol3.SBO_SIMPLE_CHEMICAL, + sbol2.BIOPAX_COMPLEX: sbol3.SBO_NON_COVALENT_COMPLEX} + types3 = [type_map.get(t, t) for t in cd2.types] + # Make the Component object and add it to the document + cp3 = sbol3.Component(cd2.identity, types3, namespace=self._sbol3_namespace(cd2), + roles=cd2.roles, sequences=cd2.sequences) + self.doc3.add(cp3) + # Convert the Component properties not covered by the constructor + if cd2.components: + raise NotImplementedError('Conversion of ComponentDefinition components ' + 'from SBOL2 to SBOL3 not yet implemented') + if cd2.sequenceAnnotations: + raise NotImplementedError('Conversion of ComponentDefinition sequenceAnnotations ' + 'from SBOL2 to SBOL3 not yet implemented') + if cd2.sequenceConstraints: + raise NotImplementedError('Conversion of ComponentDefinition sequenceConstraints ' + 'from SBOL2 to SBOL3 not yet implemented') + # Map over all other TopLevel properties and extensions not covered by the constructor + 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): + for obj in doc2.componentDefinitions: + self.visit_component_definition(obj) + for obj in doc2.moduleDefinitions: + self.visit_module_definition(obj) + for obj in doc2.models: + self.visit_model(obj) + for obj in doc2.sequences: + self.visit_sequence(obj) + for obj in doc2.collections: + self.visit_collection(obj) + for obj in doc2.activities: + self.visit_activity(obj) + for obj in doc2.plans: + self.visit_plan(obj) + for obj in doc2.agents: + self.visit_agent(obj) + for obj in doc2.attachments: + self.visit_attachment(obj) + for obj in doc2.combinatorialderivations: + self.visit_combinatorial_derivation(obj) + for obj in doc2.implementations: + self.visit_implementation(obj) + for obj in doc2.experiments: + self.visit_experiment(obj) + for obj in doc2.experimentalData: + self.visit_experimental_data(obj) + # TODO: handle "standard extensions" in pySBOL2: + # 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): + # Remap encoding if it's one of the ones that needs remapping; otherwise pass through unchanged + encoding_map = {sbol2.SBOL_ENCODING_IUPAC: sbol3.IUPAC_DNA_ENCODING, + sbol2.SBOL_ENCODING_IUPAC_PROTEIN: sbol3.IUPAC_PROTEIN_ENCODING, + sbol2.SBOL_ENCODING_SMILES: sbol3.SMILES_ENCODING} + encoding3 = encoding_map.get(seq2.encoding, seq2.encoding) + # Make the Sequence object and add it to the document + seq3 = sbol3.Sequence(seq2.identity, namespace=self._sbol3_namespace(seq2), + elements=seq2.elements, encoding=encoding3) + self.doc3.add(seq3) + # Map over all other TopLevel properties and extensions not covered by the constructor + 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') + + def convert3to2(doc3: sbol3.Document) -> sbol2.Document: """Convert an SBOL3 document to an SBOL2 document @@ -253,12 +540,12 @@ def convert3to2(doc3: sbol3.Document) -> sbol2.Document: return converter.doc2 -def convert2to3(doc2: sbol2.Document) -> sbol3.Document: +def convert2to3(doc2: sbol2.Document, namespaces=None) -> sbol3.Document: """Convert an SBOL2 document to an SBOL3 document :param doc2: SBOL2 document to convert + :param namespaces: list of URI prefixes to treat as namespaces :returns: SBOL3 document """ - doc3 = sbol3.Document() - raise NotImplementedError('Conversion from SBOL2 to SBOL3 not yet implemented') - return doc3 + converter = SBOL2To3ConversionVisitor(doc2, namespaces) + return converter.doc3 diff --git a/test/test_files/BBa_J23101.xml b/test/test_files/BBa_J23101.xml index 9b12bea5..62c8c727 100644 --- a/test/test_files/BBa_J23101.xml +++ b/test/test_files/BBa_J23101.xml @@ -58,5 +58,7 @@ igem2sbol + iGEM to SBOL conversion + Conversion of the iGEM parts registry to SBOL2.1 diff --git a/test/test_files/BBa_J23101_patched.nt b/test/test_files/BBa_J23101_patched.nt new file mode 100644 index 00000000..e17240c4 --- /dev/null +++ b/test/test_files/BBa_J23101_patched.nt @@ -0,0 +1,59 @@ + "John Anderson" . + "2006-08-03T11:00:00Z" . + "2015-08-31T04:08:40Z" . + "constitutive promoter family member" . + "BBa_J23101" . + . + . + "BBa_J23101" . + . + . + . + . + "1" . + "false" . + "true" . + . + "_52_" . + "0" . + "483" . + "95" . + "Released HQ 2013" . + "In stock" . + . + "true" . + "later" . + "N/A" . + "later" . + . + . + "true" . + . + . + . + . + "BBa_J23101_sequence" . + "tttacagctagctcagtcctaggtattatgctagc" . + . + . + "1" . + . + . + . + . + . + . + "Chris J. Myers" . + "James Alastair McLaughlin" . + "Conversion of the iGEM parts registry to SBOL2.1" . + "iGEM to SBOL conversion" . + . + "igem2sbol" . + . + "1" . + . + . + . + . + . + "2017-03-06T15:00:00+00:00" . diff --git a/test/test_sbol2_sbol3_direct.py b/test/test_sbol2_sbol3_direct.py index cb0b1688..294a2563 100644 --- a/test/test_sbol2_sbol3_direct.py +++ b/test/test_sbol2_sbol3_direct.py @@ -14,16 +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') + doc3.read(TEST_FILES / 'BBa_J23101_patched.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'))) + #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, use_native_converter=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_patched.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__':