33from Bio .Seq import Seq
44from pydna .dseqrecord import Dseqrecord
55from itertools import product
6+ from .buildcompiler import Plasmid
67from typing import List , Union , Tuple
78from .constants import DNA_TYPES
89
1112sbol2 .Config .setOption (sbol2 .ConfigOptions .SBOL_TYPED_URIS , False )
1213
1314
15+ class Assembly :
16+ """Creates an Assembly Plan.
17+
18+ :param name: Name of the assembly plan ModuleDefinition.
19+ :param part_plasmids: Parts in backbone to be assembled.
20+ :param plasmid_acceptor_backbone: Backbone in which parts are inserted on the assembly.
21+ :param restriction_enzyme: Restriction enzyme name used by PyDNA. Case sensitive, follow standard restriction enzyme nomenclature, i.e. 'BsaI'
22+ :param document: SBOL Document where the assembly plan will be created.
23+ """
24+
25+ def __init__ ( # TODO add fields for activity/agent/plan
26+ self ,
27+ part_plasmids : List [Plasmid ],
28+ backbone_plasmid : Plasmid ,
29+ restriction_enzyme : sbol2 .ComponentDefinition , # TODO search for implementation in document, or domesticate the RE
30+ ligase : sbol2 .ComponentDefinition ,
31+ document : sbol2 .Document ,
32+ ):
33+ self .part_plasmids = part_plasmids
34+ self .backbone = backbone_plasmid
35+ # self.restriction_enzyme = rebase_restriction_enzyme(restriction_enzyme)
36+ self .extracted_parts = [] # list of tuples [ComponentDefinition, Sequence]
37+ self .document = document
38+ self .assembly_activity = sbol2 .Activity ("assembly" )
39+ self .composites = []
40+
41+ def run (self ) -> List [Tuple [sbol2 .ComponentDefinition , sbol2 .Sequence ]]:
42+ """Runs full assembly simulation.
43+
44+ `document` parameter of golden_gate_assembly_plan object is updated by reference to include assembly plan ModuleDefinition and all related information.
45+
46+ Runs :func:`part_digestion` for all `part_plasmids` and :func:`backbone_digestion` for `plasmid_acceptor_backbone` with `restriction_enzyme`. Then runs :func:`ligation` with these parts to form composites.
47+
48+ :return: List of all composites generated, in the form of tuples of ComponentDefinition and Sequence.
49+ """
50+ for plasmid in self .part_plasmids :
51+ plasmid_impl = plasmid .plasmid_implementations [
52+ 0
53+ ] # TODO update with more sophisticated selection process?
54+ extracts_tuple_list , _ = part_digestion (
55+ plasmid_impl ,
56+ [self .restriction_enzyme ],
57+ self .assembly_activity ,
58+ self .document ,
59+ )
60+ # append_extracts_to_doc(extracts_tuple_list, self.document) #TODO remove this? extracted part definition should not be needed in new implementation. check to see if it's used in ligation
61+ self .extracted_parts .append (extracts_tuple_list [0 ][0 ])
62+
63+ backbone_impl = self .backbone .plasmid_implementations [0 ]
64+ extracts_tuple_list , _ = backbone_digestion (
65+ backbone_impl ,
66+ [self .restriction_enzyme ],
67+ self .assembly_activity ,
68+ self .document ,
69+ )
70+
71+ # append_extracts_to_doc(extracts_tuple_list, self.document)
72+ self .extracted_parts .append (extracts_tuple_list [0 ][0 ])
73+
74+ self .composites = ligation (
75+ self .extracted_parts , self .assembly_activity , self .document
76+ ) # TODO pass ligase into ligation as implementation, implement logic to find in impl collection otherwise 'domesticate'
77+
78+ append_extracts_to_doc (self .composites , self .document )
79+
80+ return self .composites
81+
82+
1483def rebase_restriction_enzyme (name : str , ** kwargs ) -> sbol2 .ComponentDefinition :
1584 """Creates an ComponentDefinition Restriction Enzyme Component from rebase.
1685
@@ -196,36 +265,29 @@ def is_circular(obj: sbol2.ComponentDefinition) -> bool:
196265
197266
198267def part_digestion (
199- reactant : Union [ sbol2 .ComponentDefinition , sbol2 . ModuleDefinition ] ,
268+ reactant : sbol2 .Implementation ,
200269 restriction_enzymes : List [sbol2 .ComponentDefinition ],
201- assembly_plan : sbol2 .ModuleDefinition ,
270+ assembly_activity : sbol2 .Activity ,
202271 document : sbol2 .Document ,
203272 ** kwargs ,
204273) -> Tuple [
205274 List [Tuple [sbol2 .ComponentDefinition , sbol2 .Sequence ]], sbol2 .ModuleDefinition
206275]:
207276 """Runs a simulated digestion on the top level sequence in the reactant ComponentDefinition or ModuleDefinition with the given restriciton enzymes, creating a extracted part ComponentDefinition, a digestion Interaction, and converts existing scars to 5' and 3' overhangs.
208- The product ComponentDefinition is assumed the open backbone in this case.
277+ The product ComponentDefinition is assumed the digested part in this case.
209278
210279 Written for use with the SBOL2.3 output of https://sbolcanvas.org
211280
212- :param reactant: DNA to be digested as SBOL ComponentDefinition or ModuleDefinition, usually a part_in_backbone. ComponentDefinition is the best-practice type for plasmids..
281+ :param reactant: Plasmid DNA to be digested as SBOL ComponentDefinition
213282 :param restriction_enzymes: Restriction enzymes as :class:`sbol2.ComponentDefinition`
214283 (generate with :func:`rebase_restriction_enzyme`).
215284 :param assembly_plan: SBOL ModuleDefinition to contain the functional components, interactions, and participations
216285 :param document: original SBOL2 document to be used to extract referenced objects.
217286 :return: A tuple of a list ComponentDefinitions and Sequences, and an assembly plan ModuleDefinition.
218287 """
219- if type (reactant ) is sbol2 .ModuleDefinition :
220- # extract component definition from module
221- reactant_displayId = reactant .functionalComponents [0 ].displayId
222- reactant_def_URI = reactant .functionalComponents [0 ].definition
223- reactant_component_definition = document .getComponentDefinition (
224- reactant_def_URI
225- )
226- else :
227- reactant_displayId = reactant .displayId
228- reactant_component_definition = reactant
288+
289+ reactant_component_definition = document .get (reactant .built )
290+ reactant_displayId = reactant_component_definition .displayId
229291
230292 types = set (reactant_component_definition .types or [])
231293
@@ -237,33 +299,46 @@ def part_digestion(
237299 raise ValueError (
238300 f"The reactant needs to have precisely one sequence. The input reactant has { len (reactant .sequences )} sequences"
239301 )
240- participations = []
302+ # participations = []
241303 extracts_list = []
242304 restriction_enzymes_pydna = []
243305
244306 for re in restriction_enzymes :
245307 enzyme = Restriction .__dict__ [re .name ]
246308 restriction_enzymes_pydna .append (enzyme )
247309
248- enzyme_component = sbol2 .FunctionalComponent (uri = f"{ re .name } _enzyme" )
249- enzyme_component .definition = re
250- enzyme_component .displayID = f"{ re .name } _enzyme"
251- enzyme_in_module = False
252-
253- for comp in assembly_plan .functionalComponents :
254- if comp .displayId == enzyme_component .displayID :
255- enzyme_component = comp
256- enzyme_in_module = True
257-
258- if not enzyme_in_module :
259- assembly_plan .functionalComponents .add (enzyme_component )
310+ # enzyme_component = sbol2.FunctionalComponent(uri=f"{re.name}_enzyme")
311+ # enzyme_component.definition = re
312+ # enzyme_component.displayID = f"{re.name}_enzyme"
313+
314+ enzyme_implmentation = sbol2 .Implementation (
315+ uri = f"{ re .name } _enzyme"
316+ ) # TODO take implementation in as parameter, this should be done in Restriction Enzyme domestication
317+ enzyme_implmentation .built = re
318+ enzyme_in_activity = False
319+
320+ for usage in assembly_activity .usages :
321+ entity_URI = usage .entity
322+ entity = document .get (entity_URI )
323+
324+ if entity .displayId == re .displayID :
325+ enzyme_in_activity = True
326+
327+ if not enzyme_in_activity :
328+ assembly_activity .usages .add (
329+ sbol2 .Usage (
330+ uri = f"{ re .name } _enzyme" ,
331+ entity = enzyme_implmentation ,
332+ role = "http://sbols.org/v2#build" ,
333+ )
334+ )
260335
261- modifier_participation = sbol2 .Participation (uri = "restriction" )
262- modifier_participation .participant = enzyme_component
263- modifier_participation .roles = [
264- "http://identifiers.org/biomodels.sbo/SBO:0000019"
265- ]
266- participations .append (modifier_participation )
336+ # modifier_participation = sbol2.Participation(uri="restriction")
337+ # modifier_participation.participant = enzyme_component
338+ # modifier_participation.roles = [
339+ # "http://identifiers.org/biomodels.sbo/SBO:0000019"
340+ # ]
341+ # participations.append(modifier_participation)
267342
268343 # Inform topology to PyDNA, if not found assuming linear.
269344 if is_circular (reactant_component_definition ):
@@ -284,9 +359,9 @@ def part_digestion(
284359 f"Not supported number of products. Found{ len (digested_reactant )} "
285360 )
286361 elif circular and len (digested_reactant ) == 2 :
287- part_extract , backbone = sorted (digested_reactant , key = len )
362+ part_extract , _ = sorted (digested_reactant , key = len )
288363 elif linear and len (digested_reactant ) == 3 :
289- prefix , part_extract , suffix = digested_reactant
364+ _ , part_extract , _ = digested_reactant
290365 else :
291366 raise ValueError (
292367 f"Reactant { reactant_component_definition .displayId } has no valid topology type, with { len (digested_reactant )} digested products, types: { reactant_component_definition .types } , and roles: { reactant_component_definition .roles } "
@@ -301,17 +376,19 @@ def part_digestion(
301376 )
302377 product_sequence = str (part_extract .seq )
303378 prod_component_definition , prod_seq = dna_componentdefinition_with_sequence (
304- identity = f"{ reactant . displayId if isinstance ( reactant , sbol2 . ComponentDefinition ) else reactant . functionalComponents [ 0 ] .displayId } _extracted_part" ,
379+ identity = f"{ reactant_component_definition .displayId } _extracted_part" ,
305380 sequence = product_sequence ,
306381 ** kwargs ,
307382 )
308383 prod_component_definition .wasDerivedFrom = reactant_component_definition .identity
309384 extracts_list .append ((prod_component_definition , prod_seq ))
310385
386+ # TODO explore how much granulatity in overhang representation is needed to preserve final composite annotations/components
387+
311388 # five prime overhang
312389 five_prime_oh_definition = sbol2 .ComponentDefinition (
313390 uri = f"{ reactant_displayId } _five_prime_oh"
314- ) # TODO: ensure circular type is preserved for sbh visualization
391+ )
315392 five_prime_oh_definition .addRole ("http://identifiers.org/so/SO:0001932" )
316393 five_prime_oh_location = sbol2 .Range (
317394 uri = "five_prime_oh_location" , start = 1 , end = len (product_5_prime_ss_end )
@@ -360,7 +437,7 @@ def part_digestion(
360437
361438 original_part_def_URI = ""
362439
363- # enccode ontologies of overhangs
440+ # enccode ontologies of overhangs (may no longer be necessary)
364441 for definition in document .componentDefinitions :
365442 for seqURI in definition .sequences :
366443 seq = document .getSequence (seqURI )
@@ -413,36 +490,36 @@ def part_digestion(
413490 prod_component_definition .addType ("http://identifiers.org/so/SO:0000987" ) # linear
414491
415492 # Add reference to part in backbone
416- reactant_component = sbol2 .FunctionalComponent (uri = f"{ reactant_displayId } _reactant" )
417- reactant_component .definition = reactant_component_definition
418- assembly_plan .functionalComponents .add (reactant_component )
493+ # reactant_component = sbol2.FunctionalComponent(uri=f"{reactant_displayId}_reactant")
494+ # reactant_component.definition = reactant_component_definition
495+ # assembly_plan.functionalComponents.add(reactant_component)
419496
420497 # Create reactant Participation.
421- reactant_participation = sbol2 .Participation (uri = f"{ reactant_displayId } _reactant" )
422- reactant_participation .participant = reactant_component
423- reactant_participation .roles = [sbol2 .SBO_REACTANT ]
424- participations .append (reactant_participation )
425-
426- prod_component = sbol2 .FunctionalComponent (
427- uri = f"{ reactant_displayId } _digestion_product"
428- )
429- prod_component .definition = prod_component_definition
430- assembly_plan .functionalComponents .add (prod_component )
431-
432- product_participation = sbol2 .Participation (uri = f"{ reactant_displayId } _product" )
433- product_participation .participant = prod_component
434- product_participation .roles = [sbol2 .SBO_PRODUCT ]
435- participations .append (product_participation )
498+ # reactant_participation = sbol2.Participation(uri=f"{reactant_displayId}_reactant")
499+ # reactant_participation.participant = reactant_component
500+ # reactant_participation.roles = [sbol2.SBO_REACTANT]
501+ # participations.append(reactant_participation)
502+
503+ # prod_component = sbol2.FunctionalComponent(
504+ # uri=f"{reactant_displayId}_digestion_product"
505+ # )
506+ # prod_component.definition = prod_component_definition
507+ # assembly_plan.functionalComponents.add(prod_component)
508+
509+ # product_participation = sbol2.Participation(uri=f"{reactant_displayId}_product")
510+ # product_participation.participant = prod_component
511+ # product_participation.roles = [sbol2.SBO_PRODUCT]
512+ # participations.append(product_participation)
436513
437514 # Make Interaction
438- interaction = sbol2 .Interaction (
439- uri = f"{ reactant_displayId } _digestion_interaction " ,
440- interaction_type = "http://identifiers.org/biomodels.sbo/SBO:0000178" ,
441- )
442- interaction .participations = participations
443- assembly_plan .interactions .add (interaction )
515+ # interaction = sbol2.Interaction(
516+ # uri=f"{reactant_displayId}_digestion ",
517+ # interaction_type="http://identifiers.org/biomodels.sbo/SBO:0000178",
518+ # )
519+ # interaction.participations = participations
520+ # assembly_plan.interactions.add(interaction)
444521
445- return extracts_list , assembly_plan
522+ return extracts_list , assembly_activity
446523
447524
448525def backbone_digestion (
@@ -996,7 +1073,8 @@ def ligation(
9961073
9971074 products_list .append ([composite_component_definition , composite_seq ])
9981075 composite_number += 1
999- return products_list
1076+
1077+ return products_list # TODO instead of returning list of products CDs to append to doc, append all CDs and return list of their implementations
10001078
10011079
10021080def append_extracts_to_doc (
@@ -1031,80 +1109,3 @@ def add_object_to_doc(
10311109 pass
10321110 else :
10331111 raise e
1034-
1035-
1036- class golden_gate_assembly_plan :
1037- """Creates an Assembly Plan.
1038-
1039- :param name: Name of the assembly plan ModuleDefinition.
1040- :param parts_in_backbone: Parts in backbone to be assembled.
1041- :param plasmid_acceptor_backbone: Backbone in which parts are inserted on the assembly.
1042- :param restriction_enzyme: Restriction enzyme name used by PyDNA. Case sensitive, follow standard restriction enzyme nomenclature, i.e. 'BsaI'
1043- :param document: SBOL Document where the assembly plan will be created.
1044- """
1045-
1046- def __init__ (
1047- self ,
1048- name : str ,
1049- parts_in_backbone : List [sbol2 .Document ],
1050- plasmid_acceptor_backbone : sbol2 .Document ,
1051- restriction_enzyme : str ,
1052- document : sbol2 .Document ,
1053- ):
1054- self .name = name
1055- self .parts_in_backbone = parts_in_backbone
1056- self .backbone = plasmid_acceptor_backbone
1057- self .restriction_enzyme = rebase_restriction_enzyme (restriction_enzyme )
1058- self .extracted_parts = [] # list of tuples [ComponentDefinition, Sequence]
1059- self .document = document
1060-
1061- self .assembly_plan = sbol2 .ModuleDefinition (name )
1062- self .document .add (self .assembly_plan )
1063- self .document .add (self .restriction_enzyme )
1064- self .composites = []
1065-
1066- def run (
1067- self , plasmids_in_module_definitions = False
1068- ) -> List [Tuple [sbol2 .ComponentDefinition , sbol2 .Sequence ]]:
1069- """Runs full assembly simulation.
1070-
1071- `document` parameter of golden_gate_assembly_plan object is updated by reference to include assembly plan ModuleDefinition and all related information.
1072-
1073- Runs :func:`part_digestion` for all `parts_in_backbone` and :func:`backbone_digestion` for `plasmid_acceptor_backbone` with `restriction_enzyme`. Then runs :func:`ligation` with these parts to form composites.
1074-
1075- :return: List of all composites generated, in the form of tuples of ComponentDefinition and Sequence.
1076- """
1077- for part_doc in self .parts_in_backbone :
1078- if plasmids_in_module_definitions :
1079- topLevel = part_doc .getModuleDefinition (
1080- "https://sbolcanvas.org/module1"
1081- ) # TODO change to toplevel or some other index?
1082- else :
1083- topLevel = part_doc .componentDefinitions [0 ]
1084- extracts_tuple_list , _ = part_digestion (
1085- topLevel , [self .restriction_enzyme ], self .assembly_plan , part_doc
1086- ) # make sure assembly plan is pass-by-reference
1087-
1088- append_extracts_to_doc (extracts_tuple_list , self .document )
1089- self .extracted_parts .append (extracts_tuple_list [0 ][0 ])
1090-
1091- if plasmids_in_module_definitions :
1092- topLevel = self .backbone .getModuleDefinition (
1093- "https://sbolcanvas.org/module1"
1094- ) # TODO change to toplevel or some other index?
1095- else :
1096- topLevel = self .backbone .componentDefinitions [0 ]
1097- extracts_tuple_list , _ = backbone_digestion (
1098- topLevel , [self .restriction_enzyme ], self .assembly_plan , self .backbone
1099- )
1100-
1101- append_extracts_to_doc (extracts_tuple_list , self .document )
1102- self .extracted_parts .append (extracts_tuple_list [0 ][0 ])
1103-
1104- self .composites = ligation (
1105- self .extracted_parts , self .assembly_plan , self .document
1106- )
1107-
1108- append_extracts_to_doc (self .composites , self .document )
1109-
1110- return self .composites
0 commit comments