-
Notifications
You must be signed in to change notification settings - Fork 29
Functions for Validating Composite Part Assemblies Plus Tests #117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
ac78e11
c3fdc42
a6cdd6f
87d053f
79c0279
5735eae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| import sbol3 | ||
| import tyto | ||
|
|
||
| from sbol_utilities.component import get_subcomponents, get_subcomponents_by_identity | ||
| from sbol_utilities.helper_functions import is_plasmid | ||
|
|
||
| # TODO: Delete SBOL_ASSEMBLY_PLAN and change its references to tyto.SBOL3.assemblyPlan once tyto supports the | ||
| # Design-Build-Test-Learn portion of the SBOL3 ontology | ||
| # See issues https://github.com/SynBioDex/tyto/issues/56 and https://github.com/SynBioDex/sbol-owl3/issues/5 | ||
| SBOL_ASSEMBLY_PLAN = 'http://sbols.org/v3#assemblyPlan' | ||
| ASSEMBLY_TYPES = {sbol3.SBOL_DESIGN, SBOL_ASSEMBLY_PLAN} | ||
|
|
||
|
|
||
| def validate_part_in_backbone(pib: sbol3.Component) -> bool: | ||
| """Check if a Component represents a part in backbone | ||
|
|
||
| :param plan: Component being validated | ||
| :return: true if it has SubComponents for one insert and one vector backbone | ||
| """ | ||
| subcomps = get_subcomponents(pib) | ||
|
|
||
| comps = [sc.instance_of.lookup() for sc in subcomps] | ||
|
|
||
| # Get Components for SubComponents of part in backbone that have engineered_insert as one of their roles | ||
| # (that is, a role of either the Component or its SubComponent instance) | ||
| inserts = [comps[i] for i in range(0, len(comps)) | ||
| if tyto.SO.engineered_insert in subcomps[i].roles or tyto.SO.engineered_insert in comps[i].roles] | ||
|
|
||
| # Get Components for SubComponents of part in backbone that are plasmids according to their roles | ||
| # (that is, the roles of the Components) | ||
| backbones = [c for c in comps if is_plasmid(c)] | ||
|
|
||
| return len(inserts) == 1 and len(backbones) == 1 | ||
|
|
||
|
|
||
| def validate_composite_part_assemblies(c: sbol3.Component) -> bool: | ||
| """Check if a Component for a composite part has only valid assemblies | ||
|
|
||
| :param plan: Component being validated | ||
| :return: true if all of its assemblies are valid (see validate_assembly) | ||
| """ | ||
| activities = [g.lookup() for g in c.generated_by] | ||
|
|
||
| invalid_assemblies = [a for a in activities if is_assembly(a) and not validate_assembly(a, c)] | ||
|
|
||
| return len(invalid_assemblies) == 0 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be done more efficiently by applying |
||
|
|
||
|
|
||
| def validate_assembly_component(ac: sbol3.Component, composite_part: sbol3.Component) -> bool: | ||
| """Check if Component represents the assembly of a composite part | ||
|
|
||
| :param plan: Component being validated and Component for composite part | ||
| :return: true if it has (1) SubComponents for the composite part and its assembled parts | ||
| (2) SubComponents for these parts in their backbones, and | ||
| (3) a contains Constraint for each part in backbone and its insert. | ||
| """ | ||
| # Get identities of Components that are SubComponents of the assembly Component | ||
| assembly_subcomps = get_subcomponents(ac) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need comments all through here to explain what you're doing and why |
||
| assembly_ids = {str(sc.instance_of) for sc in assembly_subcomps} | ||
|
|
||
| # Get identities of Components for assembled parts that are SubComponents of the composite part | ||
| assembled_subcomps = get_subcomponents(composite_part) | ||
| assembled_ids = {str(sc.instance_of) for sc in assembled_subcomps} | ||
|
|
||
| # Check whether composite part is SubComponent of the assembly Component | ||
| has_composite = composite_part.identity in assembly_ids | ||
|
|
||
| # Determine identities of Components for assembled parts that are not SubComponents of the assembly Component | ||
| unassembled = assembled_ids.difference(assembly_ids) | ||
|
|
||
| # Get identities of SubComponents for composite part and assembled parts in the assembly Component | ||
| for assembly_subcomponent in assembly_subcomps: | ||
| if str(assembly_subcomponent.instance_of) == composite_part.identity: | ||
| composite_subid = assembly_subcomponent.identity | ||
|
|
||
| assembled_subids = {sc.identity for sc in assembly_subcomps if str(sc.instance_of) in assembled_ids} | ||
|
|
||
| # Build map from object to subject SubComponent identities for all contains Constraints in the assembly Component | ||
| # TODO: Change sbol3.SBOL_CONTAINS to tyto.SBOL3.contains once tyto supports SBOL3 constraint restrictions | ||
| # See issues https://github.com/SynBioDex/tyto/issues/55 and https://github.com/SynBioDex/sbol-owl3/issues/4 | ||
| contained_map = {str(co.object) : str(co.subject) for co in ac.constraints if co.restriction == sbol3.SBOL_CONTAINS} | ||
|
|
||
| # Determine identities of SubComponents for assembly parts that are not the object of a contains Constraint | ||
| uncontained = assembled_subids.difference(contained_map.keys()) | ||
|
|
||
| # Add identity of SubComoponent for composite part to uncontained set if it is not the object of contains Constraint | ||
| if has_composite: | ||
| if composite_subid not in contained_map.keys(): | ||
| uncontained.add(composite_subid) | ||
|
|
||
|
|
||
| # Get identities of SubComponents for parts in backbones that contain an assembled part or composite part | ||
| pib_subids = [contained_map[key] for key in contained_map.keys() | ||
| if key in assembled_subids or (has_composite and key == composite_subid)] | ||
|
|
||
| # Get identities of Components for parts in backbones | ||
| parts_in_backbones = [sc.instance_of.lookup() for sc in get_subcomponents_by_identity(ac, pib_subids)] | ||
|
|
||
| # Determine which part in backbone Components are invalid | ||
| invalid_parts_in_backbones = [pib for pib in parts_in_backbones if not validate_part_in_backbone(pib)] | ||
|
|
||
| # ligations = [i for i in assembly_comps[0].interactions if tyto.SBO.conversion in i.types] | ||
|
|
||
| # digestions = [i for i in assembly_comps[0].interactions if tyto.SBO.cleavage in i.types] | ||
|
|
||
| return (len(unassembled) == 0 and len(uncontained) == 0 and has_composite | ||
| and len(invalid_parts_in_backbones) == 0) | ||
|
|
||
|
|
||
| def is_assembly(a: sbol3.Activity) -> bool: | ||
| """Check if Activity is an assembly | ||
|
|
||
| :param plan: Activity being checked | ||
| :return: true if it has the expected types for an assembly | ||
| """ | ||
| return set(ASSEMBLY_TYPES).issubset(a.types) | ||
|
|
||
|
|
||
| def validate_assembly(a: sbol3.Activity, composite_part: sbol3.Component) -> bool: | ||
| """Check if Activity represents the assembly of a composite part | ||
|
|
||
| :param plan: Activity being validated and Component for composite part | ||
| :return: true if it uses a single valid assembly Component (see validate_assembly_component) | ||
| """ | ||
| # TODO: Change sbol3.SBOL_Design to tyto.SBOL3.design once tyto supports the | ||
| # Design-Build-Test-Learn portion of the SBOL3 ontology | ||
| # See issues https://github.com/SynBioDex/tyto/issues/56 and https://github.com/SynBioDex/sbol-owl3/issues/5 | ||
| assembly_comps = [a.document.find(u.entity) for u in a.usage if sbol3.SBOL_DESIGN in u.roles] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need comments all through here to explain what you're doing and why. |
||
|
|
||
| is_assembly_comp_valid = True | ||
| for assembly_comp in assembly_comps: | ||
| if not validate_assembly_component(assembly_comp, composite_part): | ||
| is_assembly_comp_valid = False | ||
|
|
||
| return len(assembly_comps) == 1 and is_assembly_comp_valid | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,24 @@ | |
| from sbol_utilities.workarounds import get_parent | ||
|
|
||
|
|
||
| def get_subcomponents(c: sbol3.Component) -> List[sbol3.SubComponent]: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing docstrings |
||
| """Get all Features of Component that are SubComponents | ||
|
|
||
| :param obj: Component to get Features from | ||
| :return: List of Component Features that are SubComponents | ||
| """ | ||
| return [f for f in c.features if isinstance(f, sbol3.SubComponent)] | ||
|
|
||
|
|
||
| def get_subcomponents_by_identity(c: sbol3.Component, ids: List[str]) -> List[sbol3.SubComponent]: | ||
| """Get all SubComponents of Component that are instances of Components identified in ids | ||
|
|
||
| :param obj: Component to get Subcomponents from | ||
| :return: List of SubComponents that are instances of Components identified in ids | ||
| """ | ||
| return [sc for sc in get_subcomponents(c) if sc.identity in ids] | ||
|
|
||
|
|
||
| # TODO: consider allowing return of LocalSubComponent and ExternallyDefined | ||
| def contained_components(roots: Union[sbol3.TopLevel, Iterable[sbol3.TopLevel]]) -> Set[sbol3.Component]: | ||
| """Find the set of all SBOL Components contained within the roots or their children. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| import unittest | ||
|
|
||
| import sbol3 | ||
| import tyto | ||
| from typing import Optional | ||
|
|
||
| from sbol_utilities.build_planning import validate_composite_part_assemblies, SBOL_ASSEMBLY_PLAN | ||
|
|
||
|
|
||
| class TestBuildPlanning(unittest.TestCase): | ||
|
|
||
| def test_validate_composite_part_assemblies(self): | ||
| """Test function for validating composite part assemblies""" | ||
| test_doc = sbol3.Document() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing docstrings |
||
|
|
||
| sbol3.set_namespace('http://testBuildPlanning.org') | ||
|
|
||
| # Should work since composite part BBa_K093005 has an assembly Activity that uses an assembly Component | ||
| # that has SubComponents for BBa_K093005 and its assembled parts BBa_B0034 and BBa_E1010 and their parts in | ||
| # backbones. The assembly Component also has a contains Constraint with each part in backbone as a subject | ||
| # and the part insert as an object. | ||
| assert validate_composite_part_assemblies(assemble_BBa_K093005(test_doc)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. explain why these should or shouldn't work in comments |
||
|
|
||
| # Should not work since the assembly Component is missing a SubComponent for the assembled part BBa_E1010 | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'BBa_E1010_UNASSEMBLED')) | ||
|
|
||
| # Should not work since the assembly Component is missing a SubComponent for the composite part BBa_K093005 | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'BBa_K093005_UNASSEMBLED')) | ||
|
|
||
| # Should not work since the assembly Component has no Constraint with BBa_E1010 as its object and its part in | ||
| # backbone as its subject | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'BBa_E1010_UNCONTAINED')) | ||
|
|
||
| # Should not work since the part in backbone Component for BBa_E1010 is missing its insert SubComponent | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'BBa_E1010_NOT_INSERT')) | ||
|
|
||
| # Should not work since the part in backbone Component for BBa_E1010 is missing its backbone SubComponent | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'pSB1C3_NOT_BACKBONE')) | ||
|
|
||
| # Should not work since the assembly Activity uses more than one assembly Component | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'EXTRA_ASSEMBLY_COMPONENT')) | ||
|
|
||
| def assemble_BBa_K093005(doc: sbol3.Document, failure_mode: Optional[str] = ''): | ||
| doc = sbol3.Document() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above; need comments to explain your testing |
||
|
|
||
| sbol3.set_namespace('http://test_build_planning.org') | ||
|
|
||
| # Create assembled parts BBa_B0034 and BBa_E1010 | ||
|
|
||
| assembled_rbs = sbol3.Component('BBa_B0034', sbol3.SBO_DNA, roles=[tyto.SO.ribosome_entry_site]) | ||
| assembled_cds = sbol3.Component('BBa_E1010', sbol3.SBO_DNA, roles=[tyto.SO.CDS]) | ||
|
|
||
| doc.add(assembled_rbs) | ||
| doc.add(assembled_cds) | ||
|
|
||
| # Create composite part BBa_K093005 | ||
|
|
||
| composite_part = sbol3.Component('BBa_K093005', sbol3.SBO_DNA, roles=[tyto.SO.engineered_region]) | ||
|
|
||
| composite_part_sc1 = sbol3.SubComponent(assembled_rbs) | ||
| composite_part_sc2 = sbol3.SubComponent(assembled_cds) | ||
|
|
||
| composite_part.features += [composite_part_sc1] | ||
| composite_part.features += [composite_part_sc2] | ||
|
|
||
| doc.add(composite_part) | ||
|
|
||
| # Create backbone pSB1C3 | ||
|
|
||
| backbone = sbol3.Component('pSB1C3', [sbol3.SBO_DNA, tyto.SO.circular], | ||
| roles=[tyto.SO.plasmid_vector]) | ||
|
|
||
| doc.add(backbone) | ||
|
|
||
| # Create part in backbone for BBa_B0034 in pSB1C3 | ||
|
|
||
| rbs_in_backbone = sbol3.Component('BBa_B0034_in_pSB1C3', [sbol3.SBO_DNA, tyto.SO.circular], | ||
| roles=[tyto.SO.plasmid_vector]) | ||
|
|
||
| pib1_sc1 = sbol3.SubComponent(assembled_rbs, roles=[tyto.SO.engineered_insert]) | ||
| rbs_in_backbone.features += [pib1_sc1] | ||
| pib1_sc2 = sbol3.SubComponent(backbone) | ||
| rbs_in_backbone.features += [pib1_sc2] | ||
|
|
||
| doc.add(rbs_in_backbone) | ||
|
|
||
| # Create part in backbone for BBa_E1010 in pSB1C3 | ||
|
|
||
| cds_in_backbone = sbol3.Component('BBa_E1010_in_pSB1C3', [sbol3.SBO_DNA, tyto.SO.circular], | ||
| roles=[tyto.SO.plasmid_vector]) | ||
|
|
||
| if failure_mode != 'BBa_E1010_NOT_INSERT': | ||
| pib2_sc1 = sbol3.SubComponent(assembled_cds, roles=[tyto.SO.engineered_insert]) | ||
| cds_in_backbone.features += [pib2_sc1] | ||
|
|
||
| if failure_mode != 'pSB1C3_NOT_BACKBONE': | ||
| pib2_sc2 = sbol3.SubComponent(backbone) | ||
| cds_in_backbone.features += [pib2_sc2] | ||
|
|
||
| doc.add(cds_in_backbone) | ||
|
|
||
| # Create part in backbone for BBa_K093005 in pSB1C3 | ||
|
|
||
| gene_in_backbone = sbol3.Component('BBa_K093005_in_pSB1C3', [sbol3.SBO_DNA, tyto.SO.circular], | ||
| roles=[tyto.SO.plasmid_vector]) | ||
|
|
||
| pib3_sc1 = sbol3.SubComponent(composite_part, roles=[tyto.SO.engineered_insert]) | ||
| gene_in_backbone.features += [pib3_sc1] | ||
|
|
||
| pib3_sc2 = sbol3.SubComponent(backbone) | ||
| gene_in_backbone.features += [pib3_sc2] | ||
|
|
||
| doc.add(gene_in_backbone) | ||
|
|
||
| # Create component for assembly of BBa_K093005 | ||
|
|
||
| assembly_comp = sbol3.Component('BBa_K093005_assembly', tyto.SBO.functional_entity) | ||
|
|
||
| assembly_comp_sc1 = sbol3.SubComponent(assembled_rbs) | ||
| assembly_comp.features += [assembly_comp_sc1] | ||
| if failure_mode != 'BBa_E1010_UNASSEMBLED': | ||
| assembly_comp_sc2 = sbol3.SubComponent(assembled_cds) | ||
| assembly_comp.features += [assembly_comp_sc2] | ||
| if failure_mode != 'BBa_K093005_UNASSEMBLED': | ||
| assembly_comp_sc3 = sbol3.SubComponent(composite_part) | ||
| assembly_comp.features += [assembly_comp_sc3] | ||
| assembly_comp_sc4 = sbol3.SubComponent(rbs_in_backbone) | ||
| assembly_comp.features += [assembly_comp_sc4] | ||
| assembly_comp_sc5 = sbol3.SubComponent(cds_in_backbone) | ||
| assembly_comp.features += [assembly_comp_sc5] | ||
| assembly_comp_sc6 = sbol3.SubComponent(gene_in_backbone) | ||
| assembly_comp.features += [assembly_comp_sc6] | ||
|
|
||
| pib_contains_rbs = sbol3.Constraint(sbol3.SBOL_CONTAINS, assembly_comp_sc4, assembly_comp_sc1) | ||
| assembly_comp.constraints += [pib_contains_rbs] | ||
|
|
||
| if failure_mode != 'BBa_E1010_UNCONTAINED' and failure_mode != 'BBa_E1010_UNASSEMBLED': | ||
| pib_contains_cds = sbol3.Constraint(sbol3.SBOL_CONTAINS, assembly_comp_sc5, assembly_comp_sc2) | ||
| assembly_comp.constraints += [pib_contains_cds] | ||
|
|
||
| if failure_mode != 'BBa_K093005_UNASSEMBLED': | ||
| pib_contains_gene = sbol3.Constraint(sbol3.SBOL_CONTAINS, assembly_comp_sc6, assembly_comp_sc3) | ||
| assembly_comp.constraints += [pib_contains_gene] | ||
|
|
||
| doc.add(assembly_comp) | ||
|
|
||
| # Create activity for assembly of BBa_K093005 | ||
|
|
||
| assembly = sbol3.Activity('assemble_BBa_K093005', types=[sbol3.SBOL_DESIGN, SBOL_ASSEMBLY_PLAN]) | ||
|
|
||
| assembly_usage = sbol3.Usage(assembly_comp.identity, roles=[sbol3.SBOL_DESIGN]) | ||
| assembly.usage += [assembly_usage] | ||
|
|
||
| if failure_mode == 'EXTRA_ASSEMBLY_COMPONENT': | ||
| extra_assembly_comp = sbol3.Component('Extra_BBa_K093005_assembly', tyto.SBO.functional_entity) | ||
|
|
||
| doc.add(extra_assembly_comp) | ||
|
|
||
| extra_assembly_usage = sbol3.Usage(extra_assembly_comp.identity, roles=[sbol3.SBOL_DESIGN]) | ||
| assembly.usage += [extra_assembly_usage] | ||
|
|
||
| doc.add(assembly) | ||
|
|
||
| composite_part.generated_by += [assembly.identity] | ||
|
|
||
| return composite_part | ||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() | ||
Uh oh!
There was an error while loading. Please reload this page.