|
84 | 84 | import io |
85 | 85 | from Bio import BiopythonParserWarning |
86 | 86 | from gateway import gateway_overlap, find_gateway_sites, annotate_gateway_sites |
| 87 | +from batch_cloning.ziqiang_et_al2024 import ( |
| 88 | + router as ziqiang_et_al2024_router, |
| 89 | + validate_protospacers, |
| 90 | + design_primers as design_primers_ziqiang_et_al2024, |
| 91 | +) |
| 92 | +import json |
87 | 93 |
|
88 | 94 | # ENV variables ======================================== |
89 | 95 | RECORD_STUBS = os.environ['RECORD_STUBS'] == '1' if 'RECORD_STUBS' in os.environ else False |
@@ -1546,4 +1552,102 @@ async def get_other_frontend_files(name: str): |
1546 | 1552 | raise HTTPException(404) |
1547 | 1553 |
|
1548 | 1554 |
|
| 1555 | +@router.post('/batch_cloning/ziqiang_et_al2024', response_model=BaseCloningStrategy) |
| 1556 | +async def ziqiang_et_al2024_post(protospacers: list[str]): |
| 1557 | + try: |
| 1558 | + validate_protospacers(protospacers) |
| 1559 | + except ValueError as e: |
| 1560 | + raise HTTPException(400, str(e)) |
| 1561 | + primers = design_primers_ziqiang_et_al2024(protospacers) |
| 1562 | + |
| 1563 | + with open('batch_cloning/ziqiang_et_al2024.json', 'r') as f: |
| 1564 | + template = BaseCloningStrategy.model_validate(json.load(f)) |
| 1565 | + |
| 1566 | + max_primer_id = max([primer.id for primer in template.primers], default=0) |
| 1567 | + |
| 1568 | + for i, primer in enumerate(primers): |
| 1569 | + max_primer_id += 1 |
| 1570 | + orientation = 'rvs' if i % 2 == 0 else 'fwd' |
| 1571 | + template.primers.append( |
| 1572 | + PrimerModel(id=max_primer_id, name=f"protospacer_{i // 2 + 1}_{orientation}", sequence=primer) |
| 1573 | + ) |
| 1574 | + |
| 1575 | + primer_ids_for_pcrs = [3, *[p.id for p in template.primers[-len(primers) :]], 12] |
| 1576 | + next_node_id = max([s.id for s in template.sequences] + [s.id for s in template.sources]) + 1 |
| 1577 | + |
| 1578 | + template_sequence = next(s for s in template.sequences if s.id == 18) |
| 1579 | + for i, (fwd_primer_id, rvs_primer_id) in enumerate(zip(primer_ids_for_pcrs[::2], primer_ids_for_pcrs[1::2])): |
| 1580 | + pcr_source = PCRSource(id=next_node_id, output_name=f"pcr_protospacer_{i + 1}") |
| 1581 | + fwd_primer = next(p for p in template.primers if p.id == fwd_primer_id) |
| 1582 | + rvs_primer = next(p for p in template.primers if p.id == rvs_primer_id) |
| 1583 | + |
| 1584 | + next_node_id += 1 |
| 1585 | + resp = await pcr(pcr_source, [template_sequence], [fwd_primer, rvs_primer], 14, 0) |
| 1586 | + pcr_product: TextFileSequence = TextFileSequence.model_validate(resp['sequences'][0]) |
| 1587 | + pcr_product.id = next_node_id |
| 1588 | + pcr_source: PCRSource = PCRSource.model_validate(resp['sources'][0]) |
| 1589 | + pcr_source.output = next_node_id |
| 1590 | + |
| 1591 | + template.sequences.append(pcr_product) |
| 1592 | + template.sources.append(pcr_source) |
| 1593 | + |
| 1594 | + next_node_id += 1 |
| 1595 | + |
| 1596 | + # Find all PCR products |
| 1597 | + # (we use type instead of isinstance because the BaseCloningStrategy does not |
| 1598 | + # have the newer source models with extra methods) |
| 1599 | + pcr_product_ids = [s.output for s in template.sources if s.type == 'PCRSource'] |
| 1600 | + |
| 1601 | + # Make all input of a Golden gate assembly |
| 1602 | + golden_gate_source = RestrictionAndLigationSource( |
| 1603 | + id=next_node_id, output_name='golden_gate_assembly', restriction_enzymes=['BsaI'], input=pcr_product_ids |
| 1604 | + ) |
| 1605 | + |
| 1606 | + next_node_id += 1 |
| 1607 | + # Make them |
| 1608 | + input_sequences = [next(s for s in template.sequences if s.id == p) for p in pcr_product_ids] |
| 1609 | + resp = await restriction_and_ligation(golden_gate_source, input_sequences, False, False) |
| 1610 | + golden_gate_product: TextFileSequence = TextFileSequence.model_validate(resp['sequences'][0]) |
| 1611 | + golden_gate_product.id = next_node_id |
| 1612 | + golden_gate_source: RestrictionAndLigationSource = RestrictionAndLigationSource.model_validate(resp['sources'][0]) |
| 1613 | + golden_gate_source.output = next_node_id |
| 1614 | + next_node_id += 1 |
| 1615 | + |
| 1616 | + template.sequences.append(golden_gate_product) |
| 1617 | + template.sources.append(golden_gate_source) |
| 1618 | + |
| 1619 | + bp_target = next(s for s in template.sequences if s.id == 12) |
| 1620 | + gateway_source = GatewaySource(id=next_node_id, output_name='entry_clone', reaction_type='BP', greedy=False) |
| 1621 | + next_node_id += 1 |
| 1622 | + resp = await gateway(gateway_source, [golden_gate_product, bp_target], circular_only=True, only_multi_site=True) |
| 1623 | + gateway_product: TextFileSequence = TextFileSequence.model_validate(resp['sequences'][0]) |
| 1624 | + gateway_product.id = next_node_id |
| 1625 | + gateway_source: GatewaySource = GatewaySource.model_validate(resp['sources'][0]) |
| 1626 | + gateway_source.output = next_node_id |
| 1627 | + next_node_id += 1 |
| 1628 | + |
| 1629 | + template.sequences.append(gateway_product) |
| 1630 | + template.sources.append(gateway_source) |
| 1631 | + |
| 1632 | + # Now we want to do a Gateway with everything, so we need to find all sequences that are not input of anything |
| 1633 | + all_input_ids = sum([s.input for s in template.sources], []) |
| 1634 | + sequences_to_clone = [s for s in template.sequences if s.id not in all_input_ids] |
| 1635 | + |
| 1636 | + gateway_source = GatewaySource(id=next_node_id, output_name='expression_clone', reaction_type='LR', greedy=False) |
| 1637 | + next_node_id += 1 |
| 1638 | + resp = await gateway(gateway_source, sequences_to_clone, circular_only=True, only_multi_site=True) |
| 1639 | + index_of_product = next(i for i, s in enumerate(resp['sequences']) if '/label="Cas9"' in s.file_content) |
| 1640 | + expression_clone: TextFileSequence = TextFileSequence.model_validate(resp['sequences'][index_of_product]) |
| 1641 | + expression_clone.id = next_node_id |
| 1642 | + gateway_source: GatewaySource = GatewaySource.model_validate(resp['sources'][index_of_product]) |
| 1643 | + gateway_source.output = next_node_id |
| 1644 | + next_node_id += 1 |
| 1645 | + |
| 1646 | + template.sequences.append(expression_clone) |
| 1647 | + template.sources.append(gateway_source) |
| 1648 | + |
| 1649 | + return template |
| 1650 | + |
| 1651 | + |
1549 | 1652 | app.include_router(router) |
| 1653 | +app.include_router(ziqiang_et_al2024_router) |
0 commit comments