-
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 2 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,142 @@ | ||
| import sbol3 | ||
| import tyto | ||
|
|
||
| from sbol_utilities.component import get_subcomponents, get_subcomponents_by_identity | ||
|
|
||
| # TODO: Change SBOL_ASSEMBLY_PLAN and sbol3.SBOL_DESIGN to tyto calls after resolution of | ||
| 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 | ||
jakebeal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| :param plan: Component being validated | ||
| :return: true if its structure follows the best practices for representing a part in backbone | ||
| """ | ||
| subcomps = get_subcomponents(pib) | ||
|
|
||
| has_insert = False | ||
| has_backbone = False | ||
|
|
||
| i = 0 | ||
| while (not has_insert or not has_backbone) and i < len(subcomps): | ||
jakebeal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| part = subcomps[i].instance_of.lookup() | ||
|
|
||
| if tyto.SO.engineered_insert in subcomps[i].roles or tyto.SO.engineered_insert in part.roles: | ||
| has_insert = True | ||
| elif is_backbone(part): | ||
| has_backbone = True | ||
|
|
||
| i = i + 1 | ||
|
|
||
| return has_insert and has_backbone | ||
|
||
|
|
||
|
|
||
| def is_backbone(b: sbol3.Component) -> bool: | ||
jakebeal marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """Check if Component is a backbone | ||
|
||
|
|
||
| :param plan: Component being checked | ||
| :return: true if it has an expected role for a backbone | ||
| """ | ||
| for role in b.roles: | ||
| if role == tyto.SO.vector_replicon or tyto.SO.vector_replicon.is_ancestor_of(role): | ||
|
||
| return True | ||
|
|
||
| return False | ||
|
|
||
|
|
||
| def validate_composite_part_assemblies(c: sbol3.Component) -> bool: | ||
| """Check if a Component for a composite part has valid assemblies | ||
|
||
|
|
||
| :param plan: Component being validated | ||
| :return: true if its assemblies follows the best practices for representing a composite part | ||
| """ | ||
| 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 | ||
|
Contributor
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 follows best practices for representing assembly of composite part | ||
| """ | ||
| assembly_subcomps = get_subcomponents(ac) | ||
|
Contributor
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} | ||
|
|
||
| assembled_subcomps = get_subcomponents(composite_part) | ||
| assembled_ids = {str(sc.instance_of) for sc in assembled_subcomps} | ||
|
|
||
| has_composite = composite_part.identity in assembly_ids | ||
|
|
||
| if has_composite: | ||
| for assembly_subcomponent in assembly_subcomps: | ||
| if str(assembly_subcomponent.instance_of) == composite_part.identity: | ||
| composite_subid = assembly_subcomponent.identity | ||
|
|
||
| unassembled = assembled_ids.difference(assembly_ids) | ||
|
|
||
| assembled_subids = {sc.identity for sc in assembly_subcomps if str(sc.instance_of) in assembled_ids} | ||
|
|
||
| # TODO: Change sbol3.SBOL_CONTAINS to tyto call after resolution of | ||
| contained_map = {str(co.object) : str(co.subject) for co in ac.constraints if co.restriction == sbol3.SBOL_CONTAINS} | ||
|
|
||
| uncontained = assembled_subids.difference(contained_map.keys()) | ||
| if composite_subid not in contained_map.keys(): | ||
| uncontained.add(composite_subid) | ||
|
|
||
| pib_subids = [contained_map[key] for key in contained_map.keys() | ||
| if key in assembled_subids or key == composite_subid] | ||
| else: | ||
| unassembled = assembled_ids.difference(assembly_ids) | ||
|
|
||
| assembled_subids = {sc.identity for sc in assembly_subcomps if str(sc.instance_of) in assembled_ids} | ||
|
|
||
| # TODO: Change sbol3.SBOL_CONTAINS to tyto call after resolution of | ||
| contained_map = {str(co.object) : str(co.subject) for co in ac.constraints if co.restriction == sbol3.SBOL_CONTAINS} | ||
|
|
||
| uncontained = assembled_subids.difference(contained_map.keys()) | ||
|
|
||
| pib_subids = [contained_map[key] for key in contained_map.keys() if key in assembled_subids] | ||
|
|
||
| parts_in_backbones = [sc.instance_of.lookup() for sc in get_subcomponents_by_identity(ac, pib_subids)] | ||
|
|
||
| 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 follows best practices for representing assembly of composite part | ||
| """ | ||
| # TODO: Change sbol3.SBOL_DESIGN to tyto call after resolution of | ||
|
|
||
| assembly_comps = [a.document.find(u.entity) for u in a.usage if sbol3.SBOL_DESIGN in u.roles] | ||
|
Contributor
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 |
|---|---|---|
|
|
@@ -8,6 +8,14 @@ | |
| from sbol_utilities.workarounds import get_parent | ||
|
|
||
|
|
||
| def get_subcomponents(c: sbol3.Component) -> List[sbol3.SubComponent]: | ||
|
Contributor
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 |
||
| 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]: | ||
| 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,152 @@ | ||
| 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_doc = sbol3.Document() | ||
|
Contributor
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') | ||
|
|
||
| assert validate_composite_part_assemblies(assemble_BBa_K093005(test_doc)) | ||
|
Contributor
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 |
||
|
|
||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'BBa_E1010_UNASSEMBLED')) | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'BBa_K093005_UNASSEMBLED')) | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'BBa_E1010_UNCONTAINED')) | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'BBa_E1010_NOT_INSERT')) | ||
| assert not validate_composite_part_assemblies(assemble_BBa_K093005(test_doc, 'pSB1C3_NOT_BACKBONE')) | ||
| 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() | ||
|
Contributor
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.