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__':