def test_topo_sort_with_independent_subgraphs(self, session): canvas = Planner(session) ops1 = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", category="Cloning", ) ops2 = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", category="Cloning", ) ops3 = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", category="Cloning", ) canvas.layout.topo_sort() print([op.x for op in ops1]) print([op.x for op in ops2]) print([op.x for op in ops3]) assert ops3[0].x + canvas.layout.BOX_DELTA_X == ops2[0].x assert ops2[0].x + canvas.layout.BOX_DELTA_X == ops1[0].x
def test_align_x_with_predecessors(self, session): canvas = Planner(session) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) new_ops = canvas.get_op_by_name("E Coli Lysate") print(len(new_ops)) for _ in range(3): print("created") canvas.chain(ops[-1], "E Coli Lysate", category="Cloning") new_ops = canvas.get_op_by_name("E Coli Lysate") print(len(new_ops)) new_ops[0].x = 0 new_ops[1].x = 100 new_ops[2].x = 150 ops_layout = canvas.layout.ops_to_subgraph(ops) new_op_layout = canvas.layout.ops_to_subgraph(new_ops) midpoint = new_op_layout.midpoint() assert midpoint[0] == 75, "should be midpoint between 0 and 150" assert midpoint[0] != ops_layout.midpoint()[0] new_op_layout.align_x_midpoints_to(ops_layout) for op in new_op_layout.operations: print(op.rid) print(op.x) print() assert new_op_layout.midpoint()[0] == ops_layout.midpoint()[0]
def test_quick_wire_to_input_array(session): canvas = Planner(session) ops = canvas.chain("Purify Gel Slice", "Assemble Plasmid", category="Cloning") canvas.chain("Purify Gel Slice", ops[-1], category="Cloning") assert len(canvas.plan.operations) == 3 assert len(canvas.plan.wires) == 2
def test_quick_wire_to_input_array_with_set_sample(session): canvas = Planner(session) frags = session.Sample.where( {"sample_type_id": session.SampleType.find_by_name("Fragment").id}, opts={"limit": 10}, ) purify1 = canvas.create_operation_by_name("Purify Gel Slice", category="Cloning") purify2 = canvas.create_operation_by_name("Purify Gel Slice", category="Cloning") canvas.set_field_value(purify1.inputs[0], sample=frags[0]) canvas.set_field_value(purify2.inputs[0], sample=frags[1]) assemble = canvas.create_operation_by_name("Assemble Plasmid", category="Cloning") canvas.quick_wire(purify1, assemble) canvas.quick_wire(purify2, assemble) canvas.chain("Purify Gel Slice", assemble, category="Cloning") input_array = assemble.input_array("Fragment") assert len(input_array) == 3, "There should be 3 field values" assert input_array[0].sample == frags[0] assert input_array[1].sample == frags[1] assert input_array[2].sample is None
def test_topo_sort(self, session): canvas = Planner(session) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) ops[-1].x = 500 for _ in range(3): canvas.chain(ops[-1], ("E Coli Lysate", "Cloning"), "E Coli Colony PCR") lysate = canvas.get_op_by_name("E Coli Lysate") pcr = canvas.get_op_by_name("E Coli Colony PCR") canvas.layout.ops_to_subgraph(pcr).translate(100, 100) assert not canvas.layout.ops_to_subgraph(lysate).midpoint()[0] == ops[-1].x assert ( not canvas.layout.ops_to_subgraph(lysate).midpoint()[0] == canvas.layout.ops_to_subgraph(pcr).midpoint()[0] ) canvas.layout.topo_sort() assert canvas.layout.ops_to_subgraph(lysate).midpoint()[0] == ops[-1].x assert ( canvas.layout.ops_to_subgraph(lysate).midpoint()[0] == canvas.layout.ops_to_subgraph(pcr).midpoint()[0] )
def test_optimize(self, session): with session.with_cache() as sess: canvas = Planner(sess) q = self.sql({ "object_type_id": sess.ObjectType.find_by_name("Plasmid Stock").id, "location": self.Not("deleted"), }) item = sess.Item.one(query=q) assert item ops = canvas.chain("Make Miniprep", "Yeast Transformation", "Yeast Overnight Suspension") canvas.set_field_value_and_propogate(ops[0].inputs[0], sample=item.sample) canvas.set_to_available_item(ops[0].inputs[0]) ops = canvas.chain("Make Miniprep", "Yeast Transformation", "Yeast Overnight Suspension") canvas.set_field_value_and_propogate(ops[0].inputs[0], sample=item.sample) canvas.set_to_available_item(ops[0].inputs[0]) assert len(canvas.plan.operations) == 6 canvas.optimize() assert len(canvas.plan.operations) == 5
def test_quick_chain_to_existing_operation_too_many_times(session): canvas = Planner(session) op = canvas.create_operation_by_name("Yeast Transformation") op1 = canvas.chain(op, "Check Yeast Plate")[-1] with pytest.raises(PlannerException): canvas.chain("Yeast Transformation", op1) assert len(canvas.plan.wires) == 1
def test_canvas_quick_create_chain(session): canvas = Planner(session) canvas.chain( "Yeast Transformation", "Check Yeast Plate", "Yeast Overnight Suspension" ) assert len(canvas.plan.operations) == 3 assert len(canvas.plan.wires) == 2, "There should be two operations"
def planner_example(session): num_chains = 4 with session.with_cache() as sess: p = Planner(sess) for _ in range(num_chains): p.chain("Make PCR Fragment", "Run Gel", "Extract Gel Slice", "Purify Gel Slice") p.plan.id = 1234 return p
def test_layout(self, session): canvas = Planner(session) canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) assert canvas.layout is not None
def test_layout_edges_and_nodes(session): canvas = Planner(session) canvas.chain( "Yeast Transformation", "Check Yeast Plate", "Yeast Overnight Suspension" ) G = canvas.layout.nxgraph edges = list(G.edges) assert len(edges) == 2, "There should only be 2 edges/wires in the graph/plan" assert ( len(G.nodes) == 3 ), "There should only be 3 nodes/Operations in the graph/plan" assert edges[0][1] == edges[1][0], "Check Yeast Plate should be in both wires"
def test_optimize_case2(self, session): """Here, we are trying to optimize two chains of 5 operations. We expect to merge 10 operation to 5 operations. """ with session.with_cache() as sess: canvas = Planner(sess) q = self.sql({ "object_type_id": sess.ObjectType.find_by_name("E coli Plate of Plasmid").id, "location": self.Not("deleted"), }) item = sess.Item.one(query=q) assert item chain = [ "Check Plate", "Make Overnight Suspension", "Make Miniprep", "Yeast Transformation", "Yeast Overnight Suspension", ] yeasts = session.Sample.last( 2, query={ "sample_type_id": session.SampleType.find_by_name("Yeast Strain").id }, ) for i in range(2): ops = canvas.chain(*chain) canvas.set_field_value_and_propogate(ops[0].inputs[0], sample=item.sample) canvas.set_to_available_item(ops[0].inputs[0]) canvas.set_field_value_and_propogate(ops[-1].outputs[0], sample=yeasts[0]) canvas.set_field_value(ops[-2].inputs[1], sample=yeasts[1]) assert len(canvas.plan.operations) == 10 canvas.optimize() # we expect to merge everything except 'Yeast Transformation' and # 'Yeast Overnight' since these have absent sample definition for either # their input or outputs and FieldValues with no samples are never mergable. expected_op_types = [ "Check Plate", "Make Miniprep", "Make Overnight Suspension", "Yeast Overnight Suspension", "Yeast Transformation", ] op_types = sorted( [op.operation_type.name for op in canvas.operations]) assert len(canvas.plan.operations) == 5 assert expected_op_types == op_types
def test_roots(self, session): canvas = Planner(session) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) canvas.chain( ops[0], "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) assert len(canvas.layout.roots()) == 1
def test_canvas_chaining(session): canvas = Planner(session) canvas.browser.log.set_verbose(True) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) assert len(canvas.plan.wires) == 3 new_ops = [] for i in range(3): new_ops += canvas.chain( ops[-1], ("E Coli Lysate", "Cloning"), "E Coli Colony PCR" )[1:] assert len(canvas.plan.wires) == 2 * 3 + 3
def test_predecessor_layout(self, session): canvas = Planner(session) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) new_ops = [] for _ in range(3): new_ops += canvas.chain( ops[-1], ("E Coli Lysate", "Cloning"), "E Coli Colony PCR" )[1:] assert len(new_ops) == 6 predecessor_layout = canvas.layout.predecessor_subgraph( canvas.layout.ops_to_subgraph(new_ops) ) assert len(predecessor_layout) == 1
def test_collect_predecessors(self, session): canvas = Planner(session) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) s = canvas.layout.collect_predecessors(["r{}".format(ops[1].rid)]) assert s == ["r{}".format(ops[0].rid)]
def test_prettify(session): with session.with_cache() as sess: canvas = Planner(sess) chain = [ "Check Plate", "Make Overnight Suspension", "Make Miniprep", "Yeast Transformation", "Yeast Overnight Suspension", ] ops = canvas.chain(*chain) canvas.set_field_value_and_propogate(ops[0].inputs[0]) canvas.set_to_available_item(ops[0].inputs[0]) ops = canvas.chain(*chain) canvas.set_field_value_and_propogate(ops[0].inputs[0]) canvas.set_to_available_item(ops[0].inputs[0]) canvas.prettify()
def test_optimize_with_existing_plan(self, session): with session.with_cache() as sess: canvas = Planner(sess) q = self.sql({ "object_type_id": sess.ObjectType.find_by_name("E coli Plate of Plasmid").id, "location": self.Not("deleted"), }) item = sess.Item.one(query=q) assert item chain = [ "Check Plate", "Make Overnight Suspension", "Make Miniprep", "Yeast Transformation", "Yeast Overnight Suspension", ] ops = canvas.chain(*chain) canvas.set_field_value_and_propogate(ops[0].inputs[0], sample=item.sample) canvas.set_to_available_item(ops[0].inputs[0]) ops = canvas.chain(*chain) canvas.set_field_value_and_propogate(ops[0].inputs[0], sample=item.sample) canvas.set_to_available_item(ops[0].inputs[0]) assert len(canvas.plan.operations) == 10 canvas.save() plan_id = canvas.plan.id with session.with_cache(timeout=60) as sess: canvas = Planner(sess.Plan.find(plan_id)) canvas.optimize() assert len(canvas.plan.operations) == 7
def test_align_midpoints(self, session): canvas = Planner(session) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) new_ops = [] for _ in range(3): new_ops += canvas.chain( ops[-1], ("E Coli Lysate", "Cloning"), "E Coli Colony PCR" )[1:] layout = canvas.layout.ops_to_subgraph(ops) successors = canvas.layout.successor_subgraph(layout) ops[-1].x = 400 successors.align_x_midpoints_to(layout) assert successors.midpoint()[0] == layout.midpoint()[0]
def test_subgraph(self, session): canvas = Planner(session) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) graph = canvas.layout.ops_to_subgraph(ops[-2:]) assert len(graph) == 2 canvas.layout.topo_sort()
def test_optimization_case4(self, session): """This tests that, even if an operation has the same field_value settings, if one of its input wires is wired from a different operation_type, it will not be merged.""" with session.with_cache() as sess: canvas = Planner(sess) q = self.sql({ "object_type_id": sess.ObjectType.find_by_name("Plasmid Glycerol Stock").id, "location": self.Not("deleted"), }) item = sess.Item.one(query=q) chain1 = [ "Check Plate", "Make Overnight Suspension", "Make Miniprep", "Plasmid Digest", ] chain2 = [ "Make Overnight Suspension", "Make Miniprep", "Plasmid Digest" ] ops = canvas.chain(*chain1) canvas.set_field_value_and_propogate(ops[-1].outputs[0], sample=item.sample) ops = canvas.chain(*chain2) canvas.set_field_value_and_propogate(ops[0].inputs[0], item=item) assert len(canvas.operations) == 7 canvas.optimize() assert len(canvas.operations) == 7
def test_routing_graph(session): canvas = Planner(session) ops = canvas.chain( "Rehydrate Primer", "Make PCR Fragment", "Run Gel", "Extract Gel Slice", "Purify Gel Slice", "Assemble Plasmid", category="Cloning", ) routing_graph = canvas._routing_graph() print(get_subgraphs(routing_graph))
def test_topo_sort_chain(self, session): canvas = Planner(session) ops = canvas.chain( "Assemble Plasmid", "Transform Cells", "Plate Transformed Cells", "Check Plate", category="Cloning", ) ops[-1].x = 300 ops[-2].x = 200 ops[-3].x = 100 assert not len({op.x for op in ops}) == 1 canvas.layout.topo_sort() assert len({op.x for op in ops}) == 1
def test_optimize3(self, session): """Here we setup two operations chains of 5. 6 of these operations will be mergable (10 to 7). We also add additional operations to one of the Miniprep operations. We should end up with 9 operations after the optimization. Additionally, the merged Miniprep should have a single output with 3 wires. """ with session.with_cache() as sess: canvas = Planner(sess) q = self.sql({ "object_type_id": sess.ObjectType.find_by_name("E coli Plate of Plasmid").id, "location": self.Not("deleted"), }) item = sess.Item.one(query=q) assert item chain = [ "Check Plate", "Make Overnight Suspension", "Make Miniprep", "Yeast Transformation", "Yeast Overnight Suspension", ] ops = canvas.chain(*chain) canvas.set_field_value_and_propogate(ops[0].inputs[0], sample=item.sample) canvas.set_to_available_item(ops[0].inputs[0]) ops[-1].tagged = "YES" ops = canvas.chain(*chain) canvas.set_field_value_and_propogate(ops[0].inputs[0], sample=item.sample) canvas.set_to_available_item(ops[0].inputs[0]) canvas.chain(ops[2], "Yeast Transformation") canvas.chain(ops[2], "Make PCR Fragment") assert len(canvas.plan.operations) == 12 assert len(canvas.get_outgoing_wires(ops[2].outputs[0])) == 3 canvas.optimize() assert len(canvas.plan.operations) == 9 assert len(canvas.get_outgoing_wires(ops[2].outputs[0])) == 4 for yt in canvas.get_op_by_name("Yeast Transformation"): print(yt.inputs[0].name) assert len(canvas.get_incoming_wires(yt.inputs[0]))
def test_annotate_layout(session): canvas = Planner(session) ops = canvas.chain("Make PCR Fragment", "Run Gel", category="Cloning") canvas.layout.topo_sort() canvas.layout.move(100, 200) a = canvas.annotate_above_layout("This is an annotation", 100, 50) anchor = a["anchor"] xmidpoint = a["x"] + anchor["x"] / 2 ybottom = a["y"] + anchor["y"] assert xmidpoint == 100 + canvas.layout.BOX_WIDTH / 2 assert ybottom == 200 - canvas.layout.BOX_DELTA_Y / 2 canvas.plan.name = "annotation test" canvas.create() print(canvas.url)
def test_set_output_and_propogate(session): session.set_verbose(True) canvas = Planner(session) ops = canvas.chain( "Rehydrate Primer", "Make PCR Fragment", "Run Gel", "Extract Gel Slice", "Purify Gel Slice", "Assemble Plasmid", category="Cloning", ) example_fragment = session.Sample.find_by_name("SV40-dCas9-split") canvas.set_output_sample( ops[1].outputs[0], sample=example_fragment, setter=canvas.set_field_value_and_propogate, ) canvas.validate()
def test_optimize_case5_array_inputs_merge_missing_samples(self, session): with session.with_cache() as sess: canvas = Planner(sess) canvas.logger.set_level("DEBUG") q = self.sql({ "object_type_id": sess.ObjectType.find_by_name("Fragment Stock").id, "location": self.Not("deleted"), }) primers = sess.Sample.last( 4, query={ "sample_type_id": sess.SampleType.find_by_name("Primer").id }) fragments = sess.Sample.last( 4, query={ "sample_type_id": sess.SampleType.find_by_name("Fragment").id }) plasmids = sess.Sample.last( 3, query={ "sample_type_id": sess.SampleType.find_by_name("Plasmid").id }) subchain = [ "Make PCR Fragment", "Run Gel", "Extract Gel Slice", "Purify Gel Slice", ] ops1 = canvas.chain(*(subchain + ["Assemble Plasmid"])) ops2 = canvas.chain(*(subchain + ops1[-1:])) ops3 = canvas.chain(*(subchain + ["Assemble Plasmid"])) ops4 = canvas.chain(*(subchain + ops3[-1:])) ops5 = canvas.chain(*(subchain + ["Assemble Plasmid"])) ops6 = canvas.chain(*(subchain + ops5[-1:])) ops7 = canvas.chain(*(subchain + ops5[-1:])) ops8 = canvas.chain(*(subchain + ops5[-1:])) ops9 = canvas.chain(*(subchain + ops5[-1:])) for op in canvas.get_op_by_name("Run Gel"): pour_gel = canvas.chain("Pour Gel", op)[0] # pour_gels = [op for op in canvas.operations if # op.operation_type.name == 'Pour Gel'] # print([op.outputs[0].sample for op in pour_gels]) # chain1 using primer1 canvas.set_field_value_and_propogate(ops1[0].outputs[0], sample=fragments[0]) # chain2 using primer2 canvas.set_field_value_and_propogate(ops2[0].outputs[0], sample=fragments[1]) # chain3 using primer2 canvas.set_field_value_and_propogate(ops3[0].outputs[0], sample=fragments[1]) # chain4 using primer1 canvas.set_field_value_and_propogate(ops4[0].outputs[0], sample=fragments[0]) canvas.set_field_value_and_propogate(ops6[0].outputs[0], sample=fragments[1]) canvas.set_field_value_and_propogate(ops7[0].outputs[0], sample=fragments[1]) def pcr(op, p1, p2, t): canvas.set_field_value(op.input("Forward Primer"), sample=p1) canvas.set_field_value(op.input("Reverse Primer"), sample=p2) canvas.set_field_value(op.input("Template"), sample=t) pcr(ops1[0], primers[0], primers[1], plasmids[0]) pcr(ops2[0], primers[2], primers[3], plasmids[1]) pcr(ops4[0], primers[0], primers[1], plasmids[0]) pcr(ops3[0], primers[2], primers[3], plasmids[1]) canvas.set_field_value(ops1[-1].outputs[0], sample=plasmids[2]) canvas.set_field_value(ops3[-1].outputs[0], sample=plasmids[2]) op_types = sorted( [op.operation_type.name for op in canvas.operations]) print(op_types) canvas.optimize(merge_missing_samples=True) op_types = sorted( [op.operation_type.name for op in canvas.operations]) print(op_types) assert len(canvas.get_op_by_name("Assemble Plasmid")) == 2 assert len(canvas.get_op_by_name("Pour Gel")) == 4 for op in canvas.get_op_by_name("Assemble Plasmid"): assert len({fv.child_sample_id for fv in op.inputs}) == 2 assert len(op.inputs) in [5, 2]
def test_chain_run_gel(session): canvas = Planner(session) canvas.chain("Make PCR Fragment", "Run Gel", category="Cloning")
def test_quick_chain_to_existing_operation(session): canvas = Planner(session) op = canvas.create_operation_by_name("Yeast Transformation") canvas.chain(op, "Check Yeast Plate") assert len(canvas.plan.wires) == 1