diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b4e74a6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "python.defaultInterpreterPath": "/Users/gonzalovidal/opt/anaconda3/envs/GLLDB_py310/bin/python", + "python.terminal.activateEnvironment": true, + "jupyter.interactiveWindow.creationMode": "perFile", + "jupyter.notebookFileRoot": "${workspaceFolder}" +} diff --git a/AGENTS_log.md b/AGENTS_log.md new file mode 100644 index 0000000..c62ef9b --- /dev/null +++ b/AGENTS_log.md @@ -0,0 +1,301 @@ +# BuildCompiler Agent Handoff Log + +Last updated: 2026-07-11 + +This log captures the recent BuildCompiler work so future agents can continue +without rediscovering the same repository and PUDU context. + +## Repository Mental Model + +BuildCompiler is a workflow orchestrator. Keep stages independently callable: + +- `index_collections` +- `domestication` +- `assembly_lvl1` +- `assembly_lvl2` +- `transformation` +- `plating` +- `full_build` + +Stage contracts should stay explicit: + +- Inputs: SBOL objects/documents or JSON payloads. +- Outputs: SBOL, JSON, and protocol files. +- Avoid hidden side effects except explicitly named output files. +- Prefer deterministic JSON ordering and deterministic generated artifacts. + +## Environment + +Primary tested environment: + +```bash +/Users/gonzalovidal/opt/anaconda3/bin/conda run -n GLLDB_py310 python ... +``` + +Useful commands: + +```bash +/Users/gonzalovidal/opt/anaconda3/bin/conda run -n GLLDB_py310 python -m pytest tests/unit/adapters/pudu/test_transformation_json.py tests/unit/adapters/pudu/test_plating_json.py tests/test_buildcompiler_transformation.py +``` + +`opentrons_simulate` is available inside `GLLDB_py310`: + +```bash +/Users/gonzalovidal/opt/anaconda3/bin/conda run -n GLLDB_py310 which opentrons_simulate +``` + +PUDU local source tree used for integration: + +```text +/Users/gonzalovidal/Documents/GitHub/PUDU +``` + +When using PUDU from notebooks/scripts without installing it, prepend: + +```python +sys.path.insert(0, "/Users/gonzalovidal/Documents/GitHub/PUDU/src") +``` + +## Recent BuildCompiler Changes + +### Transformation input normalization + +File: + +- `src/buildcompiler/buildcompiler.py` + +Added `_normalize_transformation_inputs()` so `BuildCompiler.transformation()` +accepts: + +- BuildCompiler `Plasmid` objects produced by `assembly_lvl1`/`assembly_lvl2`. +- Raw `sbol2.ComponentDefinition` plasmids. +- Dict payloads with `plasmid` or `plasmid_definition`. + +Reason: the active `BuildCompiler.transformation()` method referenced this +missing helper and failed when called with level-1 assembly products. + +Regression test: + +- `tests/test_buildcompiler_transformation.py::test_transformation_accepts_lvl1_assembly_products` + +### PUDU transformation adapter + +Files: + +- `src/buildcompiler/adapters/pudu/transformation_json.py` +- `src/buildcompiler/adapters/pudu/__init__.py` +- `tests/unit/adapters/pudu/test_transformation_json.py` + +Confirmed PUDU transformation spec shape: + +```json +[ + { + "Strain": "https://SBOL2Build.org/composite_strain_1/1", + "Chassis": "https://sbolcanvas.org/DH5alpha/1", + "Plasmids": ["https://SBOL2Build.org/composite_plasmid_1/1"] + } +] +``` + +Added `plasmid_locations_to_pudu_json()` for PUDU's assembly-output location map: + +```json +{ + "https://SBOL2Build.org/composite_plasmid_1/1": ["A1"] +} +``` + +This map is consumed by PUDU transformation protocol generation as +`--plasmid-locations transformation_input.json`. + +Note: the notebook now prefers the location map produced by simulating PUDU's +assembly protocol, because that is the real handoff in the PUDU workflow. + +### PUDU plating adapter + +Files: + +- `src/buildcompiler/adapters/pudu/plating_json.py` +- `tests/unit/adapters/pudu/test_plating_json.py` + +Updated `plating_to_pudu_json()` to match PUDU directly. PUDU expects +thermocycler well names as keys: + +```json +{ + "bacterium_locations": { + "A1": [ + "composite_strain_1", + "Competent_Cell_DH5alpha", + "composite_plasmid_1", + "Media_1" + ] + } +} +``` + +Removed the old nested `advanced_parameters` wrapper from the adapter output. +Optional advanced parameters now pass through as top-level PUDU parameters. + +## Notebook Added + +New notebook: + +```text +notebooks/buildcompiler_transformation_quickstart.ipynb +``` + +It demonstrates the full offline/PUDU chain: + +1. Load local SBOL collections: + - `tests/test_files/CIDARMoCloParts_collection.xml` + - `tests/test_files/CIDARMoCloPlasmidsKit_collection.xml` + - `tests/test_files/Enzyme_Implementations_collection.xml` + - `tests/test_files/impl_test_collection.xml` +2. Load `tests/test_files/abstract_design.xml`. +3. Run BuildCompiler `assembly_lvl1`. +4. Run BuildCompiler `transformation`. +5. Write BuildCompiler SBOL and JSON artifacts. +6. Generate PUDU assembly protocol. +7. Run `opentrons_simulate pudu_assembly_protocol.py`. +8. Use generated `transformation_input.json`. +9. Generate PUDU transformation protocol. +10. Run `opentrons_simulate pudu_transformation_protocol.py`. +11. Use generated `plating_input.json`. +12. Generate PUDU plating protocol. +13. Run `opentrons_simulate pudu_plating_protocol.py`. +14. Produce final `plating_layout.json` and `plating_layout.xlsx`. + +Important implementation detail: SBOL files are written with +`Document.writeString()` and `Path.write_text()` rather than `Document.write()` +to avoid PySBOL2's online validator in offline notebook runs. + +## Generated Notebook Artifacts + +Directory: + +```text +notebooks/results/buildcompiler_transformation_quickstart/ +``` + +Current generated files include: + +- `assembly_lvl1_pudu_input.json` +- `transformation_lvl1_products.xml` +- `transformation_products.xml` +- `transformation_summary.json` +- `transformation_lvl1_pudu_input.json` +- `pudu_assembly_protocol.py` +- `pudu_assembly_protocol.simulate.log` +- `transformation_input.json` +- `pudu_transformation_protocol.py` +- `pudu_transformation_protocol.simulate.log` +- `plating_input.json` +- `pudu_plating_protocol.py` +- `pudu_plating_protocol.simulate.log` +- `plating_layout.json` +- `plating_layout.xlsx` +- `Loop Assembly.xlsx` + +The simulation logs showed the expected handoff: + +- Assembly simulation generated `transformation_input.json`. +- Transformation simulation generated `plating_input.json`. +- Plating simulation generated `plating_layout.json` and `plating_layout.xlsx`. + +## PUDU Reference Points + +Local files inspected: + +- `/Users/gonzalovidal/Documents/GitHub/PUDU/docs/guide/workflow.rst` +- `/Users/gonzalovidal/Documents/GitHub/PUDU/docs/api/transformation.rst` +- `/Users/gonzalovidal/Documents/GitHub/PUDU/src/pudu/generate_protocol.py` +- `/Users/gonzalovidal/Documents/GitHub/PUDU/src/pudu/transformation.py` +- `/Users/gonzalovidal/Documents/GitHub/PUDU/src/pudu/plating.py` +- `/Users/gonzalovidal/Documents/GitHub/PUDU/workflow_example/transformation_spec.json` +- `/Users/gonzalovidal/Documents/GitHub/PUDU/workflow_example/transformation_input.json` +- `/Users/gonzalovidal/Documents/GitHub/PUDU/workflow_example/plating_input.json` + +PUDU's documented flow: + +```text +assembly_input.json + -> pudu_assembly_protocol.py + -> opentrons_simulate + -> transformation_input.json + -> pudu_transformation_protocol.py + -> opentrons_simulate + -> plating_input.json + -> pudu_plating_protocol.py + -> opentrons_simulate + -> plating_layout.json / plating_layout.xlsx +``` + +PUDU Python API used in notebook: + +```python +from pudu.generate_protocol import detect_protocol_type, generate_protocol +``` + +PUDU CLI equivalent: + +```bash +python -m pudu.generate_protocol assembly_input.json -o assembly_protocol.py --protocol-type assembly +opentrons_simulate assembly_protocol.py + +python -m pudu.generate_protocol transformation_spec.json -o transformation_protocol.py --protocol-type transformation --plasmid-locations transformation_input.json +opentrons_simulate transformation_protocol.py + +python -m pudu.generate_protocol plating_input.json -o plating_protocol.py --protocol-type plating +opentrons_simulate plating_protocol.py +``` + +## Validation Performed + +Notebook execution: + +```bash +/Users/gonzalovidal/opt/anaconda3/bin/conda run -n GLLDB_py310 python -c "import json; ns={}; nb=json.load(open('notebooks/buildcompiler_transformation_quickstart.ipynb')); [exec(''.join(cell.get('source', [])), ns) for cell in nb['cells'] if cell.get('cell_type') == 'code']" +``` + +Adapter and regression tests: + +```bash +/Users/gonzalovidal/opt/anaconda3/bin/conda run -n GLLDB_py310 python -m pytest tests/unit/adapters/pudu/test_transformation_json.py tests/unit/adapters/pudu/test_plating_json.py tests/test_buildcompiler_transformation.py +``` + +Last observed targeted results: + +- PUDU transformation/plating adapter tests: passed. +- BuildCompiler transformation regression tests: passed. +- Full notebook simulation chain: passed. + +## Current Worktree Notes + +At the time this log was written, expected modified/untracked files included: + +- `src/buildcompiler/adapters/pudu/__init__.py` +- `src/buildcompiler/adapters/pudu/transformation_json.py` +- `src/buildcompiler/adapters/pudu/plating_json.py` +- `src/buildcompiler/buildcompiler.py` +- `tests/test_buildcompiler_transformation.py` +- `tests/unit/adapters/pudu/test_transformation_json.py` +- `tests/unit/adapters/pudu/test_plating_json.py` +- `notebooks/buildcompiler_transformation_quickstart.ipynb` +- `notebooks/results/buildcompiler_transformation_quickstart/` +- `AGENTS_log.md` + +Do not revert unrelated user changes. If these files differ from this log, +inspect before editing. + +## Next Work Suggestions + +- Add a dedicated notebook/test for full-build output feeding the same PUDU + protocol chain. +- Decide whether generated notebook results should be committed or moved to an + ignored artifact path. +- Consider a higher-level BuildCompiler helper that returns all PUDU artifacts + for a stage chain without requiring notebook glue code. +- Add optional dependency documentation for PUDU and Opentrons simulation. +- Keep PUDU as optional unless the package is intentionally added as a dependency. diff --git a/docs/Tutorials.rst b/docs/Tutorials.rst index 9e25bb6..98d58a7 100644 --- a/docs/Tutorials.rst +++ b/docs/Tutorials.rst @@ -34,4 +34,55 @@ Create golden gate assembly plan object with all the parts, the acceptor backbon assembly_plan = s2b.golden_gate_assembly_plan('tutorial_assembly_plan', [promoter, rbs, cds, terminator], backbone, 'BsaI', assembly_doc) - composites = assembly_plan.run() \ No newline at end of file + composites = assembly_plan.run() + +Full build level-2 PUDU artifact package +---------------------------------------- + +The legacy artifact-producing compiler can run a level-2 design and package all +generated SBOL, JSON, and PUDU protocol inputs into a single zip file. If the +level-2 design is missing level-1 region inputs, ``full_build`` attempts level-1 +assembly, falls back to domestication for missing parts, then retries the +downstream assemblies. + +.. code:: python + + from pathlib import Path + + import sbol2 + + from buildcompiler.buildcompiler import BuildCompiler + + test_files = Path("tests/test_files") + collection_docs = [] + for filename in ( + "CIDARMoCloParts_collection.xml", + "CIDARMoCloPlasmidsKit_collection.xml", + "Enzyme_Implementations_collection.xml", + "impl_test_collection.xml", + ): + doc = sbol2.Document() + doc.read(str(test_files / filename)) + collection_docs.append(doc) + + lvl2_design_doc = sbol2.Document() + lvl2_design_doc.read(str(test_files / "ExampleLvl2_design.xml")) + + compiler = BuildCompiler.from_local_documents( + collection_docs, + design_doc=lvl2_design_doc, + ) + result = compiler.full_build( + designs=lvl2_design_doc, + results_dir="results/full_build_lvl2_pudu", + overwrite=True, + ) + + print(result["zip_path"]) + +The zip archive includes ``full_build_manifest.json`` plus PUDU assembly, +transformation, and plating inputs/scripts such as +``assembly_lvl2_pudu_assembly_input.json``, +``assembly_lvl1_pudu_assembly_input.json``, +``domestication_pudu_assembly_input.json``, +``pudu_transformation_protocol.py``, and ``pudu_plating_protocol.py``. diff --git a/notebooks/buildcompiler_transformation_quickstart.ipynb b/notebooks/buildcompiler_transformation_quickstart.ipynb new file mode 100644 index 0000000..e0467ef --- /dev/null +++ b/notebooks/buildcompiler_transformation_quickstart.ipynb @@ -0,0 +1,324 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# BuildCompiler Transformation Quickstart\n", + "\n", + "This notebook runs a local level-1 assembly from `tests/test_files/abstract_design.xml`, then uses the assembly product as the input for chemical transformation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Imports and Local Paths\n", + "\n", + "Run this notebook from the repository checkout after installing BuildCompiler with `python -m pip install -e .`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "import subprocess\n", + "import sys\n", + "from pathlib import Path\n", + "\n", + "import sbol2\n", + "\n", + "from buildcompiler.abstract_translator import extract_toplevel_definition\n", + "from buildcompiler.adapters.pudu import plating_to_pudu_json, transformations_to_pudu_json, write_assembly_pudu_input_json\n", + "from buildcompiler.buildcompiler import BuildCompiler\n", + "\n", + "\n", + "def find_repo_root(start: Path) -> Path:\n", + " for path in [start, *start.parents]:\n", + " if (path / \"pyproject.toml\").exists() and (path / \"tests\" / \"test_files\").exists():\n", + " return path\n", + " raise RuntimeError(\"Could not find BuildCompiler repository root.\")\n", + "\n", + "\n", + "REPO_ROOT = find_repo_root(Path.cwd().resolve())\n", + "TEST_FILES = REPO_ROOT / \"tests\" / \"test_files\"\n", + "RESULTS_DIR = REPO_ROOT / \"notebooks\" / \"results\" / \"buildcompiler_transformation_quickstart\"\n", + "PUDU_REPO = REPO_ROOT.parent / \"PUDU\"\n", + "PUDU_SRC = PUDU_REPO / \"src\"\n", + "RESULTS_DIR.mkdir(parents=True, exist_ok=True)\n", + "\n", + "print(\"Repository:\", REPO_ROOT)\n", + "print(\"Results:\", RESULTS_DIR)\n", + "print(\"PUDU source:\", PUDU_SRC if PUDU_SRC.exists() else \"not found\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Load Offline SBOL Collections\n", + "\n", + "The compiler indexes local plasmids, backbones, and enzyme implementations from these fixture collections." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "collection_paths = [\n", + " TEST_FILES / \"CIDARMoCloParts_collection.xml\",\n", + " TEST_FILES / \"CIDARMoCloPlasmidsKit_collection.xml\",\n", + " TEST_FILES / \"Enzyme_Implementations_collection.xml\",\n", + " TEST_FILES / \"impl_test_collection.xml\",\n", + "]\n", + "\n", + "collection_docs = []\n", + "for path in collection_paths:\n", + " doc = sbol2.Document()\n", + " doc.read(str(path))\n", + " collection_docs.append(doc)\n", + " print(path.name, \"components=\", len(doc.componentDefinitions), \"implementations=\", len(doc.implementations))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Build Level-1 Assembly Products\n", + "\n", + "`abstract_design.xml` is resolved against the local collections and assembled into a level-1 plasmid product." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "design_doc = sbol2.Document()\n", + "design_doc.read(str(TEST_FILES / \"abstract_design.xml\"))\n", + "design = extract_toplevel_definition(design_doc)\n", + "\n", + "compiler = BuildCompiler.from_local_documents(collection_docs, design_doc=design_doc)\n", + "\n", + "assembly_doc = sbol2.Document()\n", + "assembly_routes, assembly_doc = compiler.assembly_lvl1(\n", + " [design],\n", + " final_doc=assembly_doc,\n", + " product_name=\"transformation_lvl1\",\n", + ")\n", + "assembly_products = assembly_routes[design.identity]\n", + "\n", + "lvl1_sbol_path = RESULTS_DIR / \"transformation_lvl1_products.xml\"\n", + "lvl1_pudu_path = RESULTS_DIR / \"assembly_lvl1_pudu_input.json\"\n", + "lvl1_sbol_path.write_text(assembly_doc.writeString(), encoding=\"utf-8\")\n", + "write_assembly_pudu_input_json(compiler.last_assembly_pudu_json, lvl1_pudu_path)\n", + "\n", + "print(\"Design:\", design.displayId)\n", + "print(\"Assembly products:\", [product.plasmid_definition.displayId for product in assembly_products])\n", + "print(\"Level-1 SBOL:\", lvl1_sbol_path)\n", + "print(\"Level-1 assembly PUDU input:\", lvl1_pudu_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Transform the Level-1 Product\n", + "\n", + "The transformation stage consumes the structured level-1 assembly output directly and writes PUDU-compatible transformation inputs." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "CHASSIS_NAME = \"E_coli_DH5alpha\"\n", + "PUDU_CHASSIS_URI = \"https://sbolcanvas.org/DH5alpha/1\"\n", + "\n", + "transformation_result = compiler.transformation(\n", + " assembly_products,\n", + " chassis_name=CHASSIS_NAME,\n", + " transformation_doc=assembly_doc,\n", + ")\n", + "\n", + "transformation_sbol_path = RESULTS_DIR / \"transformation_products.xml\"\n", + "transformation_summary_path = RESULTS_DIR / \"transformation_summary.json\"\n", + "transformation_pudu_path = RESULTS_DIR / \"transformation_lvl1_pudu_input.json\"\n", + "\n", + "transformation_sbol_path.write_text(assembly_doc.writeString(), encoding=\"utf-8\")\n", + "transformation_summary_path.write_text(json.dumps(transformation_result, indent=2), encoding=\"utf-8\")\n", + "\n", + "transformation_pudu = transformations_to_pudu_json(\n", + " strain_identities=[artifact[\"transformed_strain_module\"] for artifact in transformation_result[\"sbol_artifacts\"]],\n", + " chassis_identities=[PUDU_CHASSIS_URI for _ in transformation_result[\"sbol_artifacts\"]],\n", + " plasmid_sets=[[product.plasmid_definition.identity] for product in assembly_products],\n", + ")\n", + "transformation_pudu_path.write_text(json.dumps(transformation_pudu, indent=2), encoding=\"utf-8\")\n", + "\n", + "print(\"Transformation inputs:\", transformation_result[\"inputs\"])\n", + "print(\"Transformation SBOL:\", transformation_sbol_path)\n", + "print(\"Transformation summary:\", transformation_summary_path)\n", + "print(\"Transformation PUDU spec:\", transformation_pudu_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Inspect the Generated Records" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(json.dumps(transformation_pudu, indent=2))\n", + "print(\"Transformation SBOL artifacts:\")\n", + "for artifact in transformation_result[\"sbol_artifacts\"]:\n", + " print(json.dumps(artifact, indent=2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Run the PUDU Assembly to Transformation to Plating Chain\n", + "\n", + "PUDU uses `opentrons_simulate` as the handoff between stages: assembly simulation writes `transformation_input.json`, transformation simulation writes `plating_input.json`, and plating simulation writes the final plating layout files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if not PUDU_SRC.exists():\n", + " raise RuntimeError(f\"PUDU source tree not found at {PUDU_SRC}\")\n", + "if str(PUDU_SRC) not in sys.path:\n", + " sys.path.insert(0, str(PUDU_SRC))\n", + "\n", + "from pudu.generate_protocol import detect_protocol_type, generate_protocol\n", + "\n", + "\n", + "def run_opentrons_simulation(protocol_path: Path) -> Path:\n", + " env = os.environ.copy()\n", + " existing_pythonpath = env.get(\"PYTHONPATH\")\n", + " env[\"PYTHONPATH\"] = str(PUDU_SRC) if not existing_pythonpath else f\"{PUDU_SRC}{os.pathsep}{existing_pythonpath}\"\n", + " log_path = protocol_path.with_suffix(\".simulate.log\")\n", + " result = subprocess.run(\n", + " [\"opentrons_simulate\", protocol_path.name],\n", + " cwd=RESULTS_DIR,\n", + " env=env,\n", + " text=True,\n", + " capture_output=True,\n", + " check=True,\n", + " )\n", + " log_path.write_text(result.stdout + result.stderr, encoding=\"utf-8\")\n", + " return log_path\n", + "\n", + "\n", + "assembly_pudu = json.loads(lvl1_pudu_path.read_text(encoding=\"utf-8\"))\n", + "protocol_type, assembly_subtype = detect_protocol_type(assembly_pudu)\n", + "assert (protocol_type, assembly_subtype) == (\"assembly\", \"SBOL\")\n", + "pudu_assembly_protocol_path = RESULTS_DIR / \"pudu_assembly_protocol.py\"\n", + "pudu_assembly_protocol_path.write_text(\n", + " generate_protocol(\n", + " protocol_data=assembly_pudu,\n", + " protocol_type=\"assembly\",\n", + " assembly_subtype=\"SBOL\",\n", + " metadata={\n", + " \"protocolName\": \"BuildCompiler Level-1 Assembly\",\n", + " \"author\": \"BuildCompiler\",\n", + " \"description\": \"PUDU assembly generated from BuildCompiler level-1 input.\",\n", + " },\n", + " ),\n", + " encoding=\"utf-8\",\n", + ")\n", + "assembly_log_path = run_opentrons_simulation(pudu_assembly_protocol_path)\n", + "transformation_locations_path = RESULTS_DIR / \"transformation_input.json\"\n", + "plasmid_locations = json.loads(transformation_locations_path.read_text(encoding=\"utf-8\"))\n", + "\n", + "protocol_type, assembly_subtype = detect_protocol_type(transformation_pudu)\n", + "assert (protocol_type, assembly_subtype) == (\"transformation\", None)\n", + "\n", + "pudu_protocol_path = RESULTS_DIR / \"pudu_transformation_protocol.py\"\n", + "pudu_protocol_code = generate_protocol(\n", + " protocol_data=transformation_pudu,\n", + " protocol_type=\"transformation\",\n", + " plasmid_locations=plasmid_locations,\n", + " metadata={\n", + " \"protocolName\": \"BuildCompiler Level-1 Transformation\",\n", + " \"author\": \"BuildCompiler\",\n", + " \"description\": \"PUDU heat-shock transformation generated from BuildCompiler level-1 output.\",\n", + " },\n", + ")\n", + "pudu_protocol_path.write_text(pudu_protocol_code, encoding=\"utf-8\")\n", + "transformation_log_path = run_opentrons_simulation(pudu_protocol_path)\n", + "plating_input_path = RESULTS_DIR / \"plating_input.json\"\n", + "plating_input = json.loads(plating_input_path.read_text(encoding=\"utf-8\"))\n", + "plating_input = plating_to_pudu_json(bacterium_locations=plating_input[\"bacterium_locations\"])\n", + "plating_input_path.write_text(json.dumps(plating_input, indent=2), encoding=\"utf-8\")\n", + "\n", + "protocol_type, assembly_subtype = detect_protocol_type(plating_input)\n", + "assert (protocol_type, assembly_subtype) == (\"plating\", None)\n", + "pudu_plating_protocol_path = RESULTS_DIR / \"pudu_plating_protocol.py\"\n", + "pudu_plating_protocol_path.write_text(\n", + " generate_protocol(\n", + " protocol_data=plating_input,\n", + " protocol_type=\"plating\",\n", + " metadata={\n", + " \"protocolName\": \"BuildCompiler Plating\",\n", + " \"author\": \"BuildCompiler\",\n", + " \"description\": \"PUDU plating generated from BuildCompiler transformation output.\",\n", + " },\n", + " ),\n", + " encoding=\"utf-8\",\n", + ")\n", + "plating_log_path = run_opentrons_simulation(pudu_plating_protocol_path)\n", + "\n", + "print(\"Generated PUDU assembly protocol:\", pudu_assembly_protocol_path)\n", + "print(\"Assembly simulation log:\", assembly_log_path)\n", + "print(\"Assembly output for transformation:\", transformation_locations_path)\n", + "print(\"Generated PUDU protocol:\", pudu_protocol_path)\n", + "print(\"Transformation simulation log:\", transformation_log_path)\n", + "print(\"Transformation output for plating:\", plating_input_path)\n", + "print(\"Generated PUDU plating protocol:\", pudu_plating_protocol_path)\n", + "print(\"Plating simulation log:\", plating_log_path)\n", + "print(\"Final plating layout JSON:\", RESULTS_DIR / \"plating_layout.json\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/full_build_workflow_examples.ipynb b/notebooks/full_build_workflow_examples.ipynb new file mode 100644 index 0000000..f631556 --- /dev/null +++ b/notebooks/full_build_workflow_examples.ipynb @@ -0,0 +1,373 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Full Build Workflow Examples\n", + "\n", + "This notebook shows two ways to use the legacy artifact-producing `BuildCompiler.full_build(...)` workflow:\n", + "\n", + "1. A level-1 design that assembles successfully in one pass, then generates transformation and plating artifacts.\n", + "2. A level-2 design example that triggers the recovery stack: assembly level 2, assembly level 1, domestication, transformation, and plating.\n", + "\n", + "`full_build(...)` returns a dictionary with `zip_path` / `artifact_zip`. The zip contains the manifest and all generated PUDU input JSON/scripts." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports and paths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pathlib import Path\n", + "import json\n", + "import zipfile\n", + "from unittest.mock import patch\n", + "\n", + "import sbol2\n", + "\n", + "from buildcompiler.abstract_translator import extract_toplevel_definition\n", + "from buildcompiler.buildcompiler import BuildCompiler\n", + "from buildcompiler.constants import ENGINEERED_PLASMID" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ROOT = Path.cwd()\n", + "if not (ROOT / \"tests\" / \"test_files\").exists():\n", + " ROOT = ROOT.parent\n", + "\n", + "TEST_FILES = ROOT / \"tests\" / \"test_files\"\n", + "RESULTS_DIR = ROOT / \"notebooks\" / \"results\" / \"full_build_workflow_examples\"\n", + "RESULTS_DIR.mkdir(parents=True, exist_ok=True)\n", + "\n", + "def load_sbol(filename):\n", + " doc = sbol2.Document()\n", + " doc.read(str(TEST_FILES / filename))\n", + " return doc\n", + "\n", + "def list_zip(zip_path):\n", + " with zipfile.ZipFile(zip_path, \"r\") as archive:\n", + " return sorted(archive.namelist())\n", + "\n", + "def show_manifest(result):\n", + " manifest = json.loads(Path(result[\"manifest_path\"]).read_text())\n", + " print(json.dumps({\n", + " \"zip_path\": manifest[\"zip_path\"],\n", + " \"domestication\": manifest[\"domestication\"],\n", + " \"assembly_lvl1\": manifest[\"assembly_lvl1\"],\n", + " \"assembly_lvl2\": manifest[\"assembly_lvl2\"],\n", + " \"transformation_count\": len(manifest[\"transformation\"][\"successful\"]),\n", + " \"plating_count\": len(manifest[\"plating\"][\"successful\"]),\n", + " }, indent=2))\n", + "\n", + "print(TEST_FILES)\n", + "print(RESULTS_DIR)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load local SBOL inventory fixtures\n", + "\n", + "The first example uses the local SBOL fixtures in `tests/test_files` so it can run without SynBioHub credentials." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "collection_docs = [\n", + " load_sbol(\"CIDARMoCloParts_collection.xml\"),\n", + " load_sbol(\"CIDARMoCloPlasmidsKit_collection.xml\"),\n", + " load_sbol(\"Enzyme_Implementations_collection.xml\"),\n", + " load_sbol(\"impl_test_collection.xml\"),\n", + "]\n", + "\n", + "len(collection_docs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1: level-1 design succeeds in one pass\n", + "\n", + "This runs a direct level-1 full build. Since the fixture inventory contains the required part plasmids and backbone, the build should go straight through assembly level 1, transformation, plating, manifest writing, and zip packaging. Level 2 is recorded as skipped because this input is a level-1 design." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lvl1_design_doc = load_sbol(\"abstract_design.xml\")\n", + "lvl1_design = extract_toplevel_definition(lvl1_design_doc)\n", + "\n", + "lvl1_compiler = BuildCompiler.from_local_documents(\n", + " collection_docs,\n", + " design_doc=lvl1_design_doc,\n", + ")\n", + "\n", + "lvl1_result = lvl1_compiler.full_build(\n", + " designs=[lvl1_design],\n", + " results_dir=RESULTS_DIR / \"lvl1_success\",\n", + " overwrite=True,\n", + ")\n", + "\n", + "print(lvl1_result[\"zip_path\"])\n", + "show_manifest(lvl1_result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_zip(lvl1_result[\"zip_path\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 2: level-2 design triggers all stages\n", + "\n", + "The recovery path depends on the available inventory. To make the stage sequence deterministic in a notebook, this example uses a tiny SBOL level-2 design and patches the stage methods to simulate the exact condition we want to demonstrate:\n", + "\n", + "1. The first level-2 assembly attempt fails because level-1 regions are missing.\n", + "2. The first level-1 assembly attempt fails because a part needs domestication.\n", + "3. Domestication produces a part plasmid and PUDU assembly payload.\n", + "4. Level-1 assembly retries successfully.\n", + "5. Level-2 assembly retries successfully.\n", + "6. Transformation and plating artifacts are produced and packaged.\n", + "\n", + "For a real run, remove the patches and provide SBOL collections/designs that naturally produce this dependency stack." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def make_plasmid(doc, display_id):\n", + " plasmid = sbol2.ComponentDefinition(display_id)\n", + " plasmid.roles = [ENGINEERED_PLASMID]\n", + " doc.add(plasmid)\n", + " return plasmid\n", + "\n", + "def make_lvl2_doc():\n", + " doc = sbol2.Document()\n", + " tu = sbol2.ComponentDefinition(\"demo_tu\")\n", + " lvl2 = sbol2.ComponentDefinition(\"demo_lvl2_design\")\n", + " doc.add(tu)\n", + " doc.add(lvl2)\n", + " comp = lvl2.components.create(\"demo_tu_component\")\n", + " comp.definition = tu.identity\n", + " return doc\n", + "\n", + "trigger_doc = sbol2.Document()\n", + "trigger_compiler = BuildCompiler.from_local_documents([], design_doc=trigger_doc)\n", + "\n", + "missing_part = make_plasmid(trigger_compiler.sbol_doc, \"missing_promoter\")\n", + "domesticated = make_plasmid(trigger_compiler.sbol_doc, \"domesticated_missing_promoter\")\n", + "lvl1_product = make_plasmid(trigger_compiler.sbol_doc, \"assembled_demo_tu\")\n", + "lvl2_product = make_plasmid(trigger_compiler.sbol_doc, \"assembled_demo_lvl2\")\n", + "lvl2_design_doc = make_lvl2_doc()\n", + "\n", + "stage_calls = []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def fake_assembly_lvl2(*args, **kwargs):\n", + " stage_calls.append(\"assembly_lvl2\")\n", + " if stage_calls.count(\"assembly_lvl2\") == 1:\n", + " raise RuntimeError(\"level-2 input is missing level-1 regions\")\n", + " trigger_compiler.last_assembly_pudu_json_by_stage = {\n", + " \"assembly_lvl2\": [\n", + " {\n", + " \"Product\": lvl2_product.identity,\n", + " \"Backbone\": \"demo_lvl2_backbone\",\n", + " \"PartsList\": [lvl1_product.identity],\n", + " \"Restriction Enzyme\": \"BbsI\",\n", + " }\n", + " ]\n", + " }\n", + " return [lvl2_product], trigger_compiler.sbol_doc\n", + "\n", + "def fake_assembly_lvl1(*args, **kwargs):\n", + " stage_calls.append(\"assembly_lvl1\")\n", + " if stage_calls.count(\"assembly_lvl1\") == 1:\n", + " raise RuntimeError(\"level-1 input is missing a domesticated part\")\n", + " trigger_compiler.last_assembly_pudu_json_by_stage = {\n", + " \"assembly_lvl1\": [\n", + " {\n", + " \"Product\": lvl1_product.identity,\n", + " \"Backbone\": \"demo_lvl1_backbone\",\n", + " \"PartsList\": [domesticated.identity],\n", + " \"Restriction Enzyme\": \"BsaI\",\n", + " }\n", + " ]\n", + " }\n", + " return [lvl1_product], trigger_compiler.sbol_doc\n", + "\n", + "def fake_domestication(parts):\n", + " stage_calls.append(\"domestication\")\n", + " trigger_compiler.last_assembly_pudu_json_by_stage = {\n", + " \"domestication\": [\n", + " {\n", + " \"Product\": domesticated.identity,\n", + " \"Backbone\": \"demo_domestication_backbone\",\n", + " \"PartsList\": [missing_part.identity],\n", + " \"Restriction Enzyme\": \"BsaI\",\n", + " }\n", + " ]\n", + " }\n", + " return [domesticated]\n", + "\n", + "def fake_transformation(products, chassis_name=\"E_coli_DH5alpha\", transformation_doc=None):\n", + " stage_calls.append(\"transformation\")\n", + " product_id = products[0].identity\n", + " return {\n", + " \"stage\": \"transformation\",\n", + " \"chassis\": chassis_name,\n", + " \"sbol_artifacts\": [\n", + " {\n", + " \"transformed_strain_module\": f\"{product_id}_strain\",\n", + " \"transformed_strain_implementation\": f\"{product_id}_strain_impl\",\n", + " }\n", + " ],\n", + " }\n", + "\n", + "def fake_plating(*args, **kwargs):\n", + " stage_calls.append(\"plating\")\n", + " return {\n", + " \"stage\": \"plating\",\n", + " \"json_intermediate\": {\n", + " \"plating_data\": {\n", + " \"bacterium_locations\": {\"A1\": \"demo_transformed_strain\"}\n", + " }\n", + " },\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with patch.object(trigger_compiler, \"assembly_lvl2\", side_effect=fake_assembly_lvl2), \\\n", + " patch.object(trigger_compiler, \"assembly_lvl1\", side_effect=fake_assembly_lvl1), \\\n", + " patch.object(trigger_compiler, \"domestication\", side_effect=fake_domestication), \\\n", + " patch.object(trigger_compiler, \"transformation\", side_effect=fake_transformation), \\\n", + " patch.object(trigger_compiler, \"plating\", side_effect=fake_plating), \\\n", + " patch.object(trigger_compiler, \"_find_missing_parts_for_lvl1\", return_value=[{\"part\": missing_part}]):\n", + " lvl2_result = trigger_compiler.full_build(\n", + " designs=lvl2_design_doc,\n", + " results_dir=RESULTS_DIR / \"lvl2_triggers_all_stages\",\n", + " overwrite=True,\n", + " )\n", + "\n", + "print(stage_calls)\n", + "print(lvl2_result[\"zip_path\"])\n", + "show_manifest(lvl2_result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_zip(lvl2_result[\"zip_path\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Expected PUDU artifacts\n", + "\n", + "For the level-2 staged example, the zip should include at least these PUDU-facing files:\n", + "\n", + "- `assembly_lvl2_pudu_assembly_input.json`\n", + "- `assembly_lvl1_pudu_assembly_input.json`\n", + "- `domestication_pudu_assembly_input.json`\n", + "- `pudu_transformation_protocol.py`\n", + "- `transformation_pudu_input.json`\n", + "- `transformation_plasmid_locations.json`\n", + "- `pudu_plating_protocol.py`\n", + "- `plating_pudu_input.json`\n", + "- `full_build_manifest.json`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "expected = {\n", + " \"assembly_lvl2_pudu_assembly_input.json\",\n", + " \"assembly_lvl1_pudu_assembly_input.json\",\n", + " \"domestication_pudu_assembly_input.json\",\n", + " \"pudu_transformation_protocol.py\",\n", + " \"transformation_pudu_input.json\",\n", + " \"transformation_plasmid_locations.json\",\n", + " \"pudu_plating_protocol.py\",\n", + " \"plating_pudu_input.json\",\n", + " \"full_build_manifest.json\",\n", + "}\n", + "\n", + "names = set(list_zip(lvl2_result[\"zip_path\"]))\n", + "missing = sorted(expected - names)\n", + "print(\"Missing expected artifacts:\", missing)\n", + "assert not missing" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/results/buildcompiler_transformation_quickstart/Loop Assembly.xlsx b/notebooks/results/buildcompiler_transformation_quickstart/Loop Assembly.xlsx new file mode 100644 index 0000000..47c835a Binary files /dev/null and b/notebooks/results/buildcompiler_transformation_quickstart/Loop Assembly.xlsx differ diff --git a/notebooks/results/buildcompiler_transformation_quickstart/assembly_lvl1_pudu_input.json b/notebooks/results/buildcompiler_transformation_quickstart/assembly_lvl1_pudu_input.json new file mode 100644 index 0000000..5be9b9c --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/assembly_lvl1_pudu_input.json @@ -0,0 +1,13 @@ +[ + { + "Product": "http://buildcompiler.org/standard_GFP_transformation_lvl1/1", + "Backbone": "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/DVK_AE/1", + "PartsList": [ + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pJ23100_AB/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0034_BC/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pE0040_CD/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0015_DE/1" + ], + "Restriction Enzyme": "https://synbiohub.org/user/Gon/Enzyme_Implementations/BsaI/1" + } +] diff --git a/notebooks/results/buildcompiler_transformation_quickstart/plating_input.json b/notebooks/results/buildcompiler_transformation_quickstart/plating_input.json new file mode 100644 index 0000000..3e59b45 --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/plating_input.json @@ -0,0 +1,16 @@ +{ + "bacterium_locations": { + "A1": [ + "E_coli_DH5alpha_with_standard_GFP_transformation_lvl1", + "Competent_Cell_DH5alpha", + "standard_GFP_transformation_lvl1", + "Media_1" + ], + "B1": [ + "E_coli_DH5alpha_with_standard_GFP_transformation_lvl1", + "Competent_Cell_DH5alpha", + "standard_GFP_transformation_lvl1", + "Media_1" + ] + } +} \ No newline at end of file diff --git a/notebooks/results/buildcompiler_transformation_quickstart/plating_layout.json b/notebooks/results/buildcompiler_transformation_quickstart/plating_layout.json new file mode 100644 index 0000000..03cf209 --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/plating_layout.json @@ -0,0 +1,36 @@ +{ + "agar_plates": { + "plate_1": { + "dilution_1": { + "ratio": "1/10", + "wells": { + "A1": { + "construct": "E_coli_DH5alpha_with_standard_GFP_transformation_lvl1, Competent_Cell_DH5alpha, standard_GFP_transformation_lvl1, Media_1", + "source_well": "A1", + "replicate": 1 + }, + "B1": { + "construct": "E_coli_DH5alpha_with_standard_GFP_transformation_lvl1, Competent_Cell_DH5alpha, standard_GFP_transformation_lvl1, Media_1", + "source_well": "B1", + "replicate": 1 + } + } + }, + "dilution_2": { + "ratio": "1/100", + "wells": { + "A7": { + "construct": "E_coli_DH5alpha_with_standard_GFP_transformation_lvl1, Competent_Cell_DH5alpha, standard_GFP_transformation_lvl1, Media_1", + "source_well": "A1", + "replicate": 1 + }, + "B7": { + "construct": "E_coli_DH5alpha_with_standard_GFP_transformation_lvl1, Competent_Cell_DH5alpha, standard_GFP_transformation_lvl1, Media_1", + "source_well": "B1", + "replicate": 1 + } + } + } + } + } +} \ No newline at end of file diff --git a/notebooks/results/buildcompiler_transformation_quickstart/plating_layout.xlsx b/notebooks/results/buildcompiler_transformation_quickstart/plating_layout.xlsx new file mode 100644 index 0000000..de02328 Binary files /dev/null and b/notebooks/results/buildcompiler_transformation_quickstart/plating_layout.xlsx differ diff --git a/notebooks/results/buildcompiler_transformation_quickstart/pudu_assembly_protocol.py b/notebooks/results/buildcompiler_transformation_quickstart/pudu_assembly_protocol.py new file mode 100644 index 0000000..0071dfa --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/pudu_assembly_protocol.py @@ -0,0 +1,91 @@ +from pudu.assembly import SBOLLoopAssembly +from opentrons import protocol_api + + +# Protocol data +assembly_data = [ + { + 'Product': 'http://buildcompiler.org/standard_GFP_transformation_lvl1/1', + 'Backbone': 'https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/DVK_AE/1', + 'PartsList': [ + 'https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pJ23100_AB/1', + 'https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0034_BC/1', + 'https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pE0040_CD/1', + 'https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0015_DE/1' + ], + 'Restriction Enzyme': 'https://synbiohub.org/user/Gon/Enzyme_Implementations/BsaI/1' + } +] + +# Protocol metadata +metadata = { + 'protocolName': 'BuildCompiler Level-1 Assembly', + 'author': 'BuildCompiler', + 'description': 'PUDU assembly generated from BuildCompiler level-1 input.', + 'apiLevel': '2.21' +} + + +def run(protocol: protocol_api.ProtocolContext): + """Main protocol execution function""" + + protocol_instance = SBOLLoopAssembly(assembly_data=assembly_data) + protocol_instance.run(protocol) + + + +# ====================================================================== +# PARAMETER REFERENCE — SBOLLoopAssembly +# +# To customize your protocol, add any of the parameters below +# to the SBOLLoopAssembly() constructor call in run() above. +# Example: protocol_instance = SBOLLoopAssembly( +# assembly_data=assembly_data, +# replicates=3, +# initial_tip='B1', +# ) +# ====================================================================== +# +# [SBOLLoopAssembly] +# assembly_data Optional = None +# json_params Optional = None +# assemblies Optional = None +# +# [BaseAssembly] +# volume_total_reaction float = 20 +# volume_part float = 2 +# volume_restriction_enzyme float = 2 +# volume_t4_dna_ligase float = 4 +# volume_t4_dna_ligase_buffer float = 2 +# replicates int = 1 +# thermocycler_starting_well int = 0 +# thermocycler_labware str = nest_96_wellplate_100ul_pcr_full_skirt +# temperature_module_labware str = opentrons_24_aluminumblock_nest_1.5ml_snapcap +# temperature_module_position str = 1 +# tiprack_labware str = opentrons_96_tiprack_20ul +# tiprack_positions Optional = None +# pipette str = p20_single_gen2 +# pipette_position str = left +# initial_tip Optional = None +# aspiration_rate float = 0.5 +# dispense_rate float = 1 +# take_picture bool = False +# take_video bool = False +# water_testing bool = False +# output_xlsx bool = True +# protocol_name str = +# +# ---------------------------------------------------------------------- +# Full parameter descriptions: +# +# [SBOLLoopAssembly] +# SBOL Loop Assembly - handles explicit assembly dictionaries from SBOL format. +# Each assembly dictionary represents one specific construct to build. +# +# [BaseAssembly] +# Abstract base class for Loop Assembly protocols. +# Contains shared hardware setup, liquid handling, and tip management functionality. +# +# [ABC] +# Helper class that provides a standard way to create an ABC using +# inheritance. \ No newline at end of file diff --git a/notebooks/results/buildcompiler_transformation_quickstart/pudu_assembly_protocol.simulate.log b/notebooks/results/buildcompiler_transformation_quickstart/pudu_assembly_protocol.simulate.log new file mode 100644 index 0000000..eefb7f3 --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/pudu_assembly_protocol.simulate.log @@ -0,0 +1,152 @@ +Parts and reagents in temp_module +{'Deionized Water': 'A1', 'T4 DNA Ligase Buffer': 'B1', 'T4 DNA Ligase': 'C1', 'Restriction Enzyme BsaI': 'D1', 'DVK_AE': 'A2', 'pB0015_DE': 'B2', 'pB0034_BC': 'C2', 'pE0040_CD': 'D2', 'pJ23100_AB': 'A3'} +Assembled parts in thermocycler_module +{'Replicate: 1, Product: standard_GFP_transformation_lvl1': 'A1'} +DNA list for transformation protocol +['standard_GFP_transformation_lvl1_rep1'] +Protocol requires 9 tips (1 racks) +Loaded Deionized Water at position A1 +Loaded T4 DNA Ligase Buffer at position B1 +Loaded T4 DNA Ligase at position C1 +Loaded Restriction Enzyme BsaI at position D1 +Loaded DVK_AE at position A2 +Loaded pB0015_DE at position B2 +Loaded pB0034_BC at position C2 +Loaded pE0040_CD at position D2 +Loaded pJ23100_AB at position A3 +Opening Thermocycler lid +Setting Temperature Module temperature to 4.0 °C (rounded off to nearest integer) +Setting Thermocycler well block temperature to 4.0 °C +Picking up tip from A1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Aspirating 2.0 uL from A1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from B1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from B1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into B1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from B1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into B1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from B1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into B1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec +Aspirating 2.0 uL from B1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from C1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Mixing 3 times with a volume of 4.0 ul + Aspirating 4.0 uL from C1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 4.0 uL into C1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 4.0 uL from C1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 4.0 uL into C1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 4.0 uL from C1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 4.0 uL into C1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec +Aspirating 4.0 uL from C1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 4.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from D1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from D1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into D1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from D1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into D1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from D1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into D1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec +Aspirating 2.0 uL from D1 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from E1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from A2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into A2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from A2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into A2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from A2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into A2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec +Aspirating 2.0 uL from A2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from F1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from A3 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into A3 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from A3 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into A3 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from A3 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into A3 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec +Aspirating 2.0 uL from A3 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from G1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from C2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into C2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from C2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into C2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from C2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into C2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec +Aspirating 2.0 uL from C2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from H1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from D2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into D2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from D2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into D2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from D2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into D2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec +Aspirating 2.0 uL from D2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from A2 of Opentrons OT-2 96 Tip Rack 20 µL on slot 2 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from B2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into B2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from B2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into B2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Aspirating 2.0 uL from B2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec + Dispensing 2.0 uL into B2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 7.56 uL/sec +Aspirating 2.0 uL from B2 of Opentrons 24 Well Aluminum Block with NEST 1.5 mL Snapcap on Temperature Module GEN1 on slot 1 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Aspirating 20.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Dispensing 20.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Aspirating 20.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Dispensing 20.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Touching tip +Dropping tip into Trash Bin on slot 12 +Take out the reagents since the temperature module will be turn off +Closing Thermocycler lid +Setting Thermocycler lid temperature to 42.0 °C +Deactivating Temperature Module +Thermocycler starting 75 repetitions of cycle composed of the following steps: [{'temperature': 42, 'hold_time_minutes': 2}, {'temperature': 16, 'hold_time_minutes': 5}] +Thermocycler starting 1 repetitions of cycle composed of the following steps: [{'temperature': 60, 'hold_time_minutes': 10}, {'temperature': 80, 'hold_time_minutes': 10}] +Setting Thermocycler well block temperature to 4.0 °C + +====================================================================== +Generated transformation_input.json for transformation protocol + Products: 1 +====================================================================== +/Users/gonzalovidal/.opentrons/robot_settings.json not found. Loading defaults +Deck calibration not found. +/Users/gonzalovidal/.opentrons/deck_calibration.json not found. Loading defaults diff --git a/notebooks/results/buildcompiler_transformation_quickstart/pudu_plating_protocol.py b/notebooks/results/buildcompiler_transformation_quickstart/pudu_plating_protocol.py new file mode 100644 index 0000000..52707ce --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/pudu_plating_protocol.py @@ -0,0 +1,125 @@ +from pudu.plating import Plating +from opentrons import protocol_api + + +# Protocol data +plating_data = { + 'bacterium_locations': { + 'A1': [ + 'E_coli_DH5alpha_with_standard_GFP_transformation_lvl1', + 'Competent_Cell_DH5alpha', + 'standard_GFP_transformation_lvl1', + 'Media_1' + ], + 'B1': [ + 'E_coli_DH5alpha_with_standard_GFP_transformation_lvl1', + 'Competent_Cell_DH5alpha', + 'standard_GFP_transformation_lvl1', + 'Media_1' + ] + } +} + +# Protocol metadata +metadata = { + 'protocolName': 'BuildCompiler Plating', + 'author': 'BuildCompiler', + 'description': 'PUDU plating generated from BuildCompiler transformation output.', + 'apiLevel': '2.21' +} + + +def run(protocol: protocol_api.ProtocolContext): + """Main protocol execution function""" + + protocol_instance = Plating(plating_data=plating_data) + protocol_instance.run(protocol) + + + +# ====================================================================== +# PARAMETER REFERENCE — Plating +# +# To customize your protocol, add any of the parameters below +# to the Plating() constructor call in run() above. +# Example: protocol_instance = Plating( +# plating_data=plating_data, +# replicates=3, +# initial_tip='B1', +# ) +# ====================================================================== +# +# [Plating] +# plating_data Optional = None +# json_params Optional = None +# volume_total_reaction float = 20 +# volume_bacteria_transfer float = 2 +# volume_colony float = 4 +# dilution_factor float = 10 +# volume_lb float = 10000 +# replicates int = 1 +# number_dilutions int = 2 +# max_colonies int = 192 +# thermocycler_starting_well int = 0 +# thermocycler_labware str = biorad_96_wellplate_200ul_pcr +# small_tiprack str = opentrons_96_filtertiprack_20ul +# small_tiprack_position str = 9 +# initial_small_tip Optional = None +# large_tiprack str = opentrons_96_filtertiprack_200ul +# large_tiprack_position str = 1 +# initial_large_tip Optional = None +# small_pipette str = p20_single_gen2 +# small_pipette_position str = left +# large_pipette str = p300_single_gen2 +# large_pipette_position str = right +# dilution_plate str = nest_96_wellplate_100ul_pcr_full_skirt +# dilution_plate_position1 str = 2 +# dilution_plate_position2 str = 3 +# agar_plate str = nest_96_wellplate_100ul_pcr_full_skirt +# agar_plate_position1 str = 5 +# agar_plate_position2 str = 6 +# tube_rack str = opentrons_15_tuberack_falcon_15ml_conical +# tube_rack_position str = 4 +# lb_tube_position int = 0 +# aspiration_rate float = 0.5 +# dispense_rate float = 1 +# bacterium_locations Optional = None +# protocol_name str = plating_layout +# +# ---------------------------------------------------------------------- +# Full parameter descriptions: +# +# [Plating] +# Automated serial-dilution and spot-plating protocol for the Opentrons OT-2. +# +# Takes transformed bacteria from a thermocycler plate, performs up to two +# sequential 10× (or custom) dilutions in a dilution plate, and spots each +# dilution onto an agar plate. Supports multiple replicates and automatically +# distributes across two physical plates when colony counts exceed 96. +# +# After simulation, writes a JSON and an Excel file mapping each agar-plate +# well to the construct name, dilution ratio, and replicate number. +# +# Attributes: +# volume_total_reaction: Volume of bacteria loaded in each thermocycler +# source well, in µL. Used for liquid-tracking display only. +# volume_bacteria_transfer: Volume transferred from each source well into +# the dilution well, in µL. +# volume_colony: Volume spotted from each dilution well onto the agar +# plate per replicate, in µL. +# dilution_factor: Serial dilution factor applied at each step (e.g. 10 +# for a 1:10 dilution). The LB volume pre-loaded into each dilution +# well is ``volume_bacteria_transfer × (dilution_factor − 1)``. +# volume_lb: Total LB volume in the stock tube, in µL. Used for liquid +# tracking on the Opentrons deck visualiser. +# replicates: Number of agar spots per construct per dilution step. +# number_dilutions: Number of serial dilution steps to perform (max 2). +# number_constructs: Number of unique constructs derived from +# ``bacterium_locations``. +# total_colonies: Total agar wells that will be plated +# (``number_constructs × number_dilutions × replicates``). +# max_colonies: Hard cap on ``total_colonies``; raises ``ValueError`` +# if exceeded. +# bacterium_locations: Dict mapping thermocycler well names to construct +# identifiers, e.g. ``{'A1': 'GFP_construct', 'B1': ['RFP', 'v2']}``. +# protocol_name: Base name for output files (JSON and Excel). \ No newline at end of file diff --git a/notebooks/results/buildcompiler_transformation_quickstart/pudu_plating_protocol.simulate.log b/notebooks/results/buildcompiler_transformation_quickstart/pudu_plating_protocol.simulate.log new file mode 100644 index 0000000..1aa3a42 --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/pudu_plating_protocol.simulate.log @@ -0,0 +1,105 @@ +Using one plate: 2 wells per dilution fits in each half +Using one plate: 2 wells per dilution fits in each half +Setting Thermocycler well block temperature to 4.0 °C +Opening Thermocycler lid + +=== Step 1: Distributing LB to dilution wells === +Picking up tip from A1 of Opentrons OT-2 96 Filter Tip Rack 200 µL on slot 1 +Conical calculation: 10000µL remaining = 61.7mm height +Distributing to wells 1-4 +Distributing 18.0 from A1 of Opentrons 15 Tube Rack with Falcon 15 mL Conical on slot 4 to A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 + Transferring 18.0 from A1 of Opentrons 15 Tube Rack with Falcon 15 mL Conical on slot 4 to A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 + Aspirating 76.0 uL from A1 of Opentrons 15 Tube Rack with Falcon 15 mL Conical on slot 4 at 92.86 uL/sec + Dispensing 18.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 92.86 uL/sec + Dispensing 18.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 92.86 uL/sec + Dispensing 18.0 uL into A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 92.86 uL/sec + Dispensing 18.0 uL into B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 92.86 uL/sec + Blowing out into Trash Bin on slot 12 +Dropping tip into Trash Bin on slot 12 + +=== Step 2: Transferring bacteria and plating === + +Processing construct 1: ['E_coli_DH5alpha_with_standard_GFP_transformation_lvl1', 'Competent_Cell_DH5alpha', 'standard_GFP_transformation_lvl1', 'Media_1'] +Picking up tip from A1 of Opentrons OT-2 96 Filter Tip Rack 20 µL on slot 9 +Aspirating 2.0 uL from A1 of Bio-Rad 96 Well Plate 200 µL PCR on Thermocycler Module GEN1 on slot 7 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Mixing 5 times with a volume of 19.0 ul + Aspirating 19.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 3.78 uL/sec +Dispensing 2.0 uL into A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Mixing 5 times with a volume of 19.0 ul + Aspirating 19.0 uL from A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Aspirating 4.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 3.78 uL/sec +Dispensing 4.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 5 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 5 +Dropping tip into Trash Bin on slot 12 +Picking up tip from B1 of Opentrons OT-2 96 Filter Tip Rack 20 µL on slot 9 +Aspirating 4.0 uL from A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 3.78 uL/sec +Dispensing 4.0 uL into A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 5 at 7.56 uL/sec +Blowing out at A7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 5 +Dropping tip into Trash Bin on slot 12 + +Processing construct 2: ['E_coli_DH5alpha_with_standard_GFP_transformation_lvl1', 'Competent_Cell_DH5alpha', 'standard_GFP_transformation_lvl1', 'Media_1'] +Picking up tip from C1 of Opentrons OT-2 96 Filter Tip Rack 20 µL on slot 9 +Aspirating 2.0 uL from B1 of Bio-Rad 96 Well Plate 200 µL PCR on Thermocycler Module GEN1 on slot 7 at 3.78 uL/sec +Dispensing 2.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Mixing 5 times with a volume of 19.0 ul + Aspirating 19.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Aspirating 2.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 3.78 uL/sec +Dispensing 2.0 uL into B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Mixing 5 times with a volume of 19.0 ul + Aspirating 19.0 uL from B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 19.0 uL from B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 19.0 uL into B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Aspirating 4.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 3.78 uL/sec +Dispensing 4.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 5 at 7.56 uL/sec +Blowing out at B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 5 +Dropping tip into Trash Bin on slot 12 +Picking up tip from D1 of Opentrons OT-2 96 Filter Tip Rack 20 µL on slot 9 +Aspirating 4.0 uL from B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 3.78 uL/sec +Dispensing 4.0 uL into B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 5 at 7.56 uL/sec +Blowing out at B7 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 5 +Dropping tip into Trash Bin on slot 12 + +=== Plating protocol complete === +Plated 2 constructs with 1 replicates +Created a total of 4 colonies +Generated plating_layout.json +Generated plating_layout.xlsx +/Users/gonzalovidal/.opentrons/robot_settings.json not found. Loading defaults +Deck calibration not found. +/Users/gonzalovidal/.opentrons/deck_calibration.json not found. Loading defaults diff --git a/notebooks/results/buildcompiler_transformation_quickstart/pudu_transformation_protocol.py b/notebooks/results/buildcompiler_transformation_quickstart/pudu_transformation_protocol.py new file mode 100644 index 0000000..a76c2f2 --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/pudu_transformation_protocol.py @@ -0,0 +1,217 @@ +from pudu.transformation import HeatShockTransformation +from opentrons import protocol_api + + +# Protocol data +transformation_data = [ + { + 'Strain': 'http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_transformation_lvl1/1', + 'Chassis': 'https://sbolcanvas.org/DH5alpha/1', + 'Plasmids': ['http://buildcompiler.org/standard_GFP_transformation_lvl1/1'] + } +] + +# Plasmid well locations from assembly protocol output +plasmid_locations = { + 'http://buildcompiler.org/standard_GFP_transformation_lvl1/1': ['A1'] +} + +# Protocol metadata +metadata = { + 'protocolName': 'BuildCompiler Level-1 Transformation', + 'author': 'BuildCompiler', + 'description': 'PUDU heat-shock transformation generated from BuildCompiler level-1 output.', + 'apiLevel': '2.21' +} + + +def run(protocol: protocol_api.ProtocolContext): + """Main protocol execution function""" + + protocol_instance = HeatShockTransformation( + transformation_data=transformation_data, + plasmid_locations=plasmid_locations + ) + protocol_instance.run(protocol) + + + +# ====================================================================== +# PARAMETER REFERENCE — HeatShockTransformation +# +# To customize your protocol, add any of the parameters below +# to the HeatShockTransformation() constructor call in run() above. +# Example: protocol_instance = HeatShockTransformation( +# transformation_data=transformation_data, +# replicates=3, +# initial_tip='B1', +# ) +# ====================================================================== +# +# [HeatShockTransformation] +# transformation_data Optional = None +# plasmid_locations Optional = None +# json_params Optional = None +# transfer_volume_dna float = 2 +# transfer_volume_competent_cell float = 20 +# tube_volume_competent_cell float = 100 +# transfer_volume_recovery_media float = 60 +# tube_volume_recovery_media float = 1200 +# cold_incubation1 Optional = None +# heat_shock Optional = None +# cold_incubation2 Optional = None +# recovery_incubation Optional = None +# +# [Transformation] +# volume_dna float = 20 +# replicates int = 2 +# thermocycler_starting_well int = 0 +# thermocycler_labware str = nest_96_wellplate_100ul_pcr_full_skirt +# temperature_module_labware str = opentrons_24_aluminumblock_nest_1.5ml_snapcap +# temperature_module_position str = 1 +# dna_plate str = nest_96_wellplate_100ul_pcr_full_skirt +# dna_plate_position str = 2 +# use_dna_96plate bool = False +# tiprack_p20_labware str = opentrons_96_tiprack_20ul +# tiprack_p20_position str = 9 +# tiprack_p200_labware str = opentrons_96_filtertiprack_200ul +# tiprack_p200_position str = 6 +# pipette_p20 str = p20_single_gen2 +# pipette_p20_position str = left +# pipette_p300 str = p300_single_gen2 +# pipette_p300_position str = right +# aspiration_rate float = 0.5 +# dispense_rate float = 1 +# initial_dna_well int = 0 +# water_testing bool = False +# initial_tip_p20 Optional = None +# initial_tip_p300 Optional = None +# tube_rack_labware str = opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap +# tube_rack_position str = 3 +# +# ---------------------------------------------------------------------- +# Full parameter descriptions: +# +# [HeatShockTransformation] +# Heat shock transformation protocol for the Opentrons OT-2. +# +# Automates the full heat shock transformation workflow: loading DNA and competent +# cells into a thermocycler plate, running the heat shock cycle, adding recovery +# media, and exporting a plating map for the next protocol step. +# +# Inherits all base parameters from Transformation. The attributes below are +# specific to the heat shock transformation protocol. +# +# Attributes +# ---------- +# transfer_volume_dna : float +# Volume of DNA to transfer into each thermocycler well, in microliters. +# By default, 2 microliters. Note: this is the volume actually pipetted per +# reaction, distinct from volume_dna (the volume loaded into the source well). +# transfer_volume_competent_cell : float +# Volume of competent cells to transfer into each thermocycler well, in +# microliters. By default, 20 microliters. +# tube_volume_competent_cell : float +# Total usable volume of competent cells per tube, in microliters. Used to +# calculate how many reactions each tube can supply before switching to the +# next tube. By default, 100 microliters. +# transfer_volume_recovery_media : float +# Volume of recovery media to add to each well after heat shock, in +# microliters. By default, 60 microliters. +# tube_volume_recovery_media : float +# Total usable volume of recovery media per tube, in microliters. Used to +# calculate how many wells each tube can supply. By default, 1200 microliters. +# cold_incubation1 : dict +# First cold incubation step (on ice before heat shock). A dict with keys +# 'temperature' (°C) and 'hold_time_minutes'. +# By default, {'temperature': 4, 'hold_time_minutes': 30}. +# heat_shock : dict +# Heat shock step. A dict with keys 'temperature' (°C) and 'hold_time_minutes'. +# By default, {'temperature': 42, 'hold_time_minutes': 1}. +# cold_incubation2 : dict +# Second cold incubation immediately after heat shock. A dict with keys +# 'temperature' (°C) and 'hold_time_minutes'. +# By default, {'temperature': 4, 'hold_time_minutes': 2}. +# recovery_incubation : dict +# Recovery incubation after recovery media addition. A dict with keys +# 'temperature' (°C) and 'hold_time_minutes'. +# By default, {'temperature': 37, 'hold_time_minutes': 60}. +# +# [Transformation] +# Base class for automated transformation protocols on the Opentrons OT-2. +# +# Handles loading transformation data, validating parameters, and providing +# shared utilities used by all transformation subclasses. Subclasses implement +# the specific thermocycler workflow (e.g. heat shock). +# +# Attributes +# ---------- +# volume_dna : float +# Volume of DNA loaded into each source well, in microliters. By default, +# 20 microliters. We suggest 2 µL for extracted plasmid and 5 µL for PCR +# products when setting transfer_volume_dna in the subclass. +# replicates : int +# Number of transformation replicates per strain per assembly location. +# By default, 2. +# thermocycler_starting_well : int +# Zero-indexed starting well in the thermocycler plate. By default, 0 (well A1). +# thermocycler_labware : str +# Labware type for the thermocycler plate. +# By default, 'nest_96_wellplate_100ul_pcr_full_skirt'. +# temperature_module_labware : str +# Labware type for the aluminum block on the temperature module. +# By default, 'opentrons_24_aluminumblock_nest_1.5ml_snapcap'. +# temperature_module_position : str +# Deck slot for the temperature module. By default, '1'. +# dna_plate : str +# Labware type for the 96-well DNA source plate (used when use_dna_96plate=True). +# By default, 'nest_96_wellplate_100ul_pcr_full_skirt'. +# dna_plate_position : str +# Deck slot for the 96-well DNA source plate. By default, '2'. +# use_dna_96plate : bool +# If True, DNA is sourced from a 96-well plate at fixed positions given by +# plasmid_locations. Automatically set to True when plasmid_locations is +# provided. By default, False. +# tiprack_p20_labware : str +# Labware type for the p20 tip rack. By default, 'opentrons_96_tiprack_20ul'. +# tiprack_p20_position : str +# Deck slot for the p20 tip rack. By default, '9'. +# tiprack_p200_labware : str +# Labware type for the p200 tip rack. +# By default, 'opentrons_96_filtertiprack_200ul'. +# tiprack_p200_position : str +# Deck slot for the p200 tip rack. By default, '6'. +# pipette_p20 : str +# Pipette model for the p20 single-channel. By default, 'p20_single_gen2'. +# pipette_p20_position : str +# Mount for the p20 pipette ('left' or 'right'). By default, 'left'. +# pipette_p300 : str +# Pipette model for the p300 single-channel. By default, 'p300_single_gen2'. +# pipette_p300_position : str +# Mount for the p300 pipette ('left' or 'right'). By default, 'right'. +# aspiration_rate : float +# Relative aspiration speed as a fraction of the pipette's maximum flow +# rate, where 1.0 is full speed and 0.5 is half speed. Lower values +# reduce bubble formation. By default, 0.5. +# dispense_rate : float +# Relative dispense speed as a fraction of the pipette's maximum flow +# rate, where 1.0 is full speed. By default, 1.0. +# initial_dna_well : int +# Zero-indexed starting well for DNA tubes on the aluminum block (used when +# use_dna_96plate=False). By default, 0. +# water_testing : bool +# If True, uses water in place of competent cells and recovery media during +# simulation/testing runs. By default, False. +# initial_tip_p20 : str, optional +# Well name of the first tip to use from the p20 tip rack (e.g. 'B1'). +# If None, starts from the first available tip. By default, None. +# initial_tip_p300 : str, optional +# Well name of the first tip to use from the p300 tip rack (e.g. 'C3'). +# If None, starts from the first available tip. By default, None. +# tube_rack_labware : str +# Labware type for the tube rack that holds competent cells and recovery +# media. Moving these off the temperature module frees the entire aluminum +# block for DNA plasmids, maximising unique constructs per run. +# By default, 'opentrons_24_tuberack_eppendorf_1.5ml_safelock_snapcap'. +# tube_rack_position : str +# Deck slot for the tube rack. By default, '3'. \ No newline at end of file diff --git a/notebooks/results/buildcompiler_transformation_quickstart/pudu_transformation_protocol.simulate.log b/notebooks/results/buildcompiler_transformation_quickstart/pudu_transformation_protocol.simulate.log new file mode 100644 index 0000000..949672b --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/pudu_transformation_protocol.simulate.log @@ -0,0 +1,78 @@ +DNA constructs in DNA plate +{'standard_GFP_transformation_lvl1': ['A1']} +Competent cells and media in tube rack +{'Competent Cell DH5alpha_1': 'A1', 'Media_1': 'B1'} +Genetically modified organisms in thermocycler +{'A1': ['E_coli_DH5alpha_with_standard_GFP_transformation_lvl1', 'Competent_Cell_DH5alpha', 'standard_GFP_transformation_lvl1', 'Media_1'], 'B1': ['E_coli_DH5alpha_with_standard_GFP_transformation_lvl1', 'Competent_Cell_DH5alpha', 'standard_GFP_transformation_lvl1', 'Media_1']} +Simulation detected - enabling water testing mode +Opening Thermocycler lid +Distributing 20.0 from A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 to A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 + Transferring 20.0 from A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 to A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 + Picking up tip from A1 of Opentrons OT-2 96 Filter Tip Rack 200 µL on slot 6 + Mixing 3 times with a volume of 50.0 ul + Aspirating 50.0 uL from A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Dispensing 50.0 uL into A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Aspirating 50.0 uL from A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Dispensing 50.0 uL into A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Aspirating 50.0 uL from A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Dispensing 50.0 uL into A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Aspirating 40.0 uL from A1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Dispensing 20.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 92.86 uL/sec + Dispensing 20.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 92.86 uL/sec + Dropping tip into Trash Bin on slot 12 +Picking up tip from A1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 9 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 3.78 uL/sec +Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Aspirating 20.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Dispensing 20.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Aspirating 20.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Dispensing 20.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Touching tip +Dropping tip into Trash Bin on slot 12 +Picking up tip from B1 of Opentrons OT-2 96 Tip Rack 20 µL on slot 9 +Mixing 3 times with a volume of 2.0 ul + Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec + Dispensing 2.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 7.56 uL/sec +Aspirating 2.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on slot 2 at 3.78 uL/sec +Dispensing 2.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Blowing out at B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 +Aspirating 20.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Dispensing 20.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Aspirating 20.0 uL from B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Dispensing 20.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 7.56 uL/sec +Touching tip +Dropping tip into Trash Bin on slot 12 +Closing Thermocycler lid +Opening Thermocycler lid +Distributing 60.0 from B1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 to A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 + Transferring 60.0 from B1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 to A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 + Picking up tip from B1 of Opentrons OT-2 96 Filter Tip Rack 200 µL on slot 6 + Aspirating 120.0 uL from B1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Air gap of 10 uL + Aspirating 10.0 uL from B1 of Opentrons 24 Tube Rack with Eppendorf 1.5 mL Safe-Lock Snapcap on slot 3 at 92.86 uL/sec + Dispensing 70.0 uL into A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 92.86 uL/sec + Air gap of 10 uL + Aspirating 10.0 uL from A1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 92.86 uL/sec + Dispensing 70.0 uL into B1 of NEST 96 Well Plate 100 µL PCR Full Skirt on Thermocycler Module GEN1 on slot 7 at 92.86 uL/sec + Dropping tip into Trash Bin on slot 12 +Closing Thermocycler lid + +====================================================================== +Generated plating_input.json for next protocol + Bacteria locations: 2 +====================================================================== +/Users/gonzalovidal/.opentrons/robot_settings.json not found. Loading defaults +Deck calibration not found. +/Users/gonzalovidal/.opentrons/deck_calibration.json not found. Loading defaults diff --git a/notebooks/results/buildcompiler_transformation_quickstart/transformation_input.json b/notebooks/results/buildcompiler_transformation_quickstart/transformation_input.json new file mode 100644 index 0000000..195ee0f --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/transformation_input.json @@ -0,0 +1,5 @@ +{ + "http://buildcompiler.org/standard_GFP_transformation_lvl1/1": [ + "A1" + ] +} \ No newline at end of file diff --git a/notebooks/results/buildcompiler_transformation_quickstart/transformation_lvl1_products.xml b/notebooks/results/buildcompiler_transformation_quickstart/transformation_lvl1_products.xml new file mode 100644 index 0000000..17363de --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/transformation_lvl1_products.xml @@ -0,0 +1,550 @@ + + + + + + + Ligation_Scar_A + 1 + + + + + + Ligation_Scar_B + + + 1 + + + + + + 1 + + + + + + Ligation_Scar_C + + + + + + + Ligation_Scar_D + + 1 + + + + + + 1 + + Ligation_Scar_E + + + + + + + + + + 1 + + + J23100 + + + + + + + B0015_3_annotation + 1 + + + B0015_3_location + 1 + 793 + 921 + + + + + + + + + + Ligation_Scar_D_annotation + + + + 1 + 792 + 789 + + Ligation_Scar_D_location + + + 1 + + + + + + + + + + Ligation_Scar_C_B0034 + + + 1 + + + standard_GFP_transformation_lvl1 + + + + + + + 1 + 1 + 4 + + Ligation_Scar_A_location + + + Ligation_Scar_A_annotation + 1 + + + + + + + + Ligation_Scar_E + + 1 + + + + + + + 1 + Ligation_Scar_A + + + + + + + 1 + + B0015 + + + + + + + + Ligation_Scar_E_B0015 + + 1 + + + + + + + + + + 3152 + 926 + 1 + + dvk_backbone_core_5_location + + + + dvk_backbone_core_5_annotation + 1 + + + + + + dvk_backbone_core + + 1 + + + + + + + + + Ligation_Scar_E_annotation + + + + 922 + Ligation_Scar_E_location + 925 + 1 + + + + 1 + + + + + Ligation_Scar_C_annotation + + + + 1 + + 68 + Ligation_Scar_C_location + + 65 + + + 1 + + + + + + B0034_Ligation_Scar_B + + 1 + + + + + + + + + + + 69 + 1 + + 788 + E0040m_gfp_3_location + + + + + E0040m_gfp_3_annotation + 1 + + + standard_GFP_transformation_lvl1 + + + + + 1 + E0040m_gfp + + + + + + + + + + 1 + 43 + + Ligation_Scar_B_location + 40 + + + 1 + + + Ligation_Scar_B_annotation + + + + + + 1 + + + Ligation_Scar_B + + + + + B0034 + + 1 + + + + + + + 1 + + + + + dvk_backbone_core_Ligation_Scar_E + + + + + + + 1 + B0034_3_location + 64 + + + 44 + + + B0034_3_annotation + + + 1 + + + + + + + J23100_Ligation_Scar_A + 1 + + + + + + + + J23100_3_annotation + 1 + + + + + J23100_3_location + 5 + + + 39 + 1 + + + + + + + + + 1 + + + Ligation_Scar_D_E0040m_gfp + + + 1 + + + + + Ligation_Scar_B_J23100 + + 1 + + + + + + 1 + + + B0015_Ligation_Scar_D + + + + + + + + + + 1 + Ligation_Scar_C + + + + + + Ligation_Scar_D + 1 + + + + + + + 1 + + E0040m_gfp_Ligation_Scar_C + + + + + + + + + + 1 + Ligation_Scar_A_sequence + GGAG + + + + + 1 + Ligation_Scar_B_sequence + TACT + + + + 1 + + Ligation_Scar_C_sequence + AATG + + + + + Ligation_Scar_D_sequence + 1 + AGGT + + + + + Ligation_Scar_E_sequence + 1 + GCTT + + + + GGAGTTGACGGCTAGCTCAGTCCTAGGTACAGTGCTAGCTACTAGAGAAAGAGGAGAAATACTAAATGATGCGTAAAGGAGAAGAACTTTTCACTGGAGTTGTCCCAATTCTTGTTGAATTAGATGGTGATGTTAATGGGCACAAATTTTCTGTCAGTGGAGAGGGTGAAGGTGATGCAACATACGGAAAACTTACCCTTAAATTTATTTGCACTACTGGAAAACTACCTGTTCCATGGCCAACACTTGTCACTACTTTCGGTTATGGTGTTCAATGCTTTGCGAGATACCCAGATCATATGAAACAGCATGACTTTTTCAAGAGTGCCATGCCCGAAGGTTATGTACAGGAAAGAACTATATTTTTCAAAGATGACGGGAACTACAAGACACGTGCTGAAGTCAAGTTTGAAGGTGATACCCTTGTTAATAGAATCGAGTTAAAAGGTATTGATTTTAAAGAAGATGGAAACATTCTTGGACACAAATTGGAATACAACTATAACTCACACAATGTATACATCATGGCAGACAAACAAAAGAATGGAATCAAAGTTAACTTCAAAATTAGACACAACATTGAAGATGGAAGCGTTCAACTAGCAGACCATTATCAACAAAATACTCCAATTGGCGATGGCCCTGTCCTTTTACCAGACAACCATTACCTGTCCACACAATCTGCCCTTTCGAAAGATCCCAACGAAAAGAGAGATCACATGGTCCTTCTTGAGTTTGTAACAGCTGCTGGGATTACACATGGCATGGATGAACTATACAAATAATAAAGGTCCAGGCATCAAATAAAACGAAAGGCTCAGTCGAAAGACTGGGCCTTTCGTTTTATCTGTTGTTTGTCGGTGAACGCTCTCTACTAGAGTCACACTGGCTCACCTTCGGGTGGGCCTTTCTGCGTTTATAGCTTatgtcttctactagtagcggccgctgcagtccggcaaaaaagggcaaggtgtcaccaccctgccctttttctttaaaaccgaaaagattacttcgcgttatgcaggcttcctcgctcactgactcgctgcgctcggtcgttcggctgcggcgagcggtatcagctcactcaaaggcggtaatacggttatccacagaatcaggggataacgcaggaaagaacatgtgagcaaaaggccagcaaaaggccaggaaccgtaaaaaggccgcgttgctggcgtttttccacaggctccgcccccctgacgagcatcacaaaaatcgacgctcaagtcagaggtggcgaaacccgacaggactataaagataccaggcgtttccccctggaagctccctcgtgcgctctcctgttccgaccctgccgcttaccggatacctgtccgcctttctcccttcgggaagcgtggcgctttctcatagctcacgctgtaggtatctcagttcggtgtaggtcgttcgctccaagctgggctgtgtgcacgaaccccccgttcagcccgaccgctgcgccttatccggtaactatcgtcttgagtccaacccggtaagacacgacttatcgccactggcagcagccactggtaacaggattagcagagcgaggtatgtaggcggtgctacagagttcttgaagtggtggcctaactacggctacactagaagaacagtatttggtatctgcgctctgctgaagccagttaccttcggaaaaagagttggtagctcttgatccggcaaacaaaccaccgctggtagcggtggtttttttgtttgcaagcagcagattacgcgcagaaaaaaaggatctcaagaagatcctttgatcttttctacggggtctgacgctcagtggaacgaaaactcacgttaagggattttggtcatgagattatcaaaaaggatcttcacctagatccttttaaattaaaaatgaagttttaaatcaatctaaagtatatatgagtaaacttggtctgacagctcgagtcccgtcaagtcagcgtaatgctctgccagtgttacaaccaattaaccaattctgattagaaaaactcatcgagcatcaaatgaaactgcaatttattcatatcaggattatcaataccatatttttgaaaaagccgtttctgtaatgaaggagaaaactcaccgaggcagttccataggatggcaagatcctggtatcggtctgcgattccgactcgtccaacatcaatacaacctattaatttcccctcgtcaaaaataaggttatcaagtgagaaatcaccatgagtgacgactgaatccggtgagaatggcaaaagcttatgcatttctttccagacttgttcaacaggccagccattacgctcgtcatcaaaatcactcgcatcaaccaaaccgttattcattcgtgattgcgcctgagcgagacgaaatacgcgatcgctgttaaaaggacaattacaaacaggaatcgaatgcaaccggcgcaggaacactgccagcgcatcaacaatattttcacctgaatcaggatattcttctaatacctggaatgctgttttcccggggatcgcagtggtgagtaaccatgcatcatcaggagtacggataaaatgcttgatggtcggaagaggcataaattccgtcagccagtttagtctgaccatctcatctgtaacatcattggcaacgctacctttgccatgtttcagaaacaactctggcgcatcgggcttcccatacaatcgatagattgtcgcacctgattgcccgacattatcgcgagcccatttatacccatataaatcagcatccatgttggaatttaatcgcggcctggagcaagacgtttcccgttgaatatggctcataacaccccttgtattactgtttatgtaagcagacagttttattgttcatgatgatatatttttatcttgtgcaatgtaacatcagagattttgagacacaacgtggctttgttgaataaatcgaacttttgctgagttgaaggatcagctcgagtgccacctgacgtctaagaaaccattattatcatgacattaacctataaaaataggcgtatcacgaggcagaatttcagataaaaaaaatccttagctttcgctaaggatgatttctggaattcgcggccgcttctagagactagtggaagacat + + standard_GFP_transformation_lvl1_seq + 1 + + + 1 + standard_GFP_transformation_lvl1_impl + + + + + + 1 + + + 1 + + + pB0015_DE_impl + + + + DNA Assembly + + + pE0040_CD_impl + + 1 + + + + + + + + + T4_Ligase + 1 + + + + + + + + pB0034_BC_impl + 1 + + + + + + + + + + 1 + DVK_AE_impl + + + + + 1 + + + BsaI_enzyme + + + + + + + + + 1 + + assemble_ + + + standard_GFP_transformation_lvl1_assembly + + + + 1 + + pJ23100_AB_impl + + + + + diff --git a/notebooks/results/buildcompiler_transformation_quickstart/transformation_lvl1_pudu_input.json b/notebooks/results/buildcompiler_transformation_quickstart/transformation_lvl1_pudu_input.json new file mode 100644 index 0000000..67eaa63 --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/transformation_lvl1_pudu_input.json @@ -0,0 +1,9 @@ +[ + { + "Strain": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_transformation_lvl1/1", + "Chassis": "https://sbolcanvas.org/DH5alpha/1", + "Plasmids": [ + "http://buildcompiler.org/standard_GFP_transformation_lvl1/1" + ] + } +] \ No newline at end of file diff --git a/notebooks/results/buildcompiler_transformation_quickstart/transformation_products.xml b/notebooks/results/buildcompiler_transformation_quickstart/transformation_products.xml new file mode 100644 index 0000000..ccb41eb --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/transformation_products.xml @@ -0,0 +1,619 @@ + + + + + + + Ligation_Scar_A + 1 + + + + + + Ligation_Scar_B + + + 1 + + + + + + 1 + + + + + + Ligation_Scar_C + + + + + + + Ligation_Scar_D + + 1 + + + + + + 1 + + Ligation_Scar_E + + + + + + + + + + 1 + + + J23100 + + + + + + + B0015_3_annotation + 1 + + + B0015_3_location + 1 + 793 + 921 + + + + + + + + + + Ligation_Scar_D_annotation + + + + 1 + 792 + 789 + + Ligation_Scar_D_location + + + 1 + + + + + + + + + + Ligation_Scar_C_B0034 + + + 1 + + + standard_GFP_transformation_lvl1 + + + + + + + 1 + 1 + 4 + + Ligation_Scar_A_location + + + Ligation_Scar_A_annotation + 1 + + + + + + + + Ligation_Scar_E + + 1 + + + + + + + 1 + Ligation_Scar_A + + + + + + + 1 + + B0015 + + + + + + + + Ligation_Scar_E_B0015 + + 1 + + + + + + + + + + 3152 + 926 + 1 + + dvk_backbone_core_5_location + + + + dvk_backbone_core_5_annotation + 1 + + + + + + dvk_backbone_core + + 1 + + + + + + + + + Ligation_Scar_E_annotation + + + + 922 + Ligation_Scar_E_location + 925 + 1 + + + + 1 + + + + + Ligation_Scar_C_annotation + + + + 1 + + 68 + Ligation_Scar_C_location + + 65 + + + 1 + + + + + + B0034_Ligation_Scar_B + + 1 + + + + + + + + + + + 69 + 1 + + 788 + E0040m_gfp_3_location + + + + + E0040m_gfp_3_annotation + 1 + + + standard_GFP_transformation_lvl1 + + + + + 1 + E0040m_gfp + + + + + + + + + + 1 + 43 + + Ligation_Scar_B_location + 40 + + + 1 + + + Ligation_Scar_B_annotation + + + + + + 1 + + + Ligation_Scar_B + + + + + B0034 + + 1 + + + + + + + 1 + + + + + dvk_backbone_core_Ligation_Scar_E + + + + + + + 1 + B0034_3_location + 64 + + + 44 + + + B0034_3_annotation + + + 1 + + + + + + + J23100_Ligation_Scar_A + 1 + + + + + + + + J23100_3_annotation + 1 + + + + + J23100_3_location + 5 + + + 39 + 1 + + + + + + + + + 1 + + + Ligation_Scar_D_E0040m_gfp + + + 1 + + + + + Ligation_Scar_B_J23100 + + 1 + + + + + + 1 + + + B0015_Ligation_Scar_D + + + + + + + + + + 1 + Ligation_Scar_C + + + + + + Ligation_Scar_D + 1 + + + + + + + 1 + + E0040m_gfp_Ligation_Scar_C + + + + + + + + + + 1 + Ligation_Scar_A_sequence + GGAG + + + + + 1 + Ligation_Scar_B_sequence + TACT + + + + 1 + + Ligation_Scar_C_sequence + AATG + + + + + Ligation_Scar_D_sequence + 1 + AGGT + + + + + Ligation_Scar_E_sequence + 1 + GCTT + + + + GGAGTTGACGGCTAGCTCAGTCCTAGGTACAGTGCTAGCTACTAGAGAAAGAGGAGAAATACTAAATGATGCGTAAAGGAGAAGAACTTTTCACTGGAGTTGTCCCAATTCTTGTTGAATTAGATGGTGATGTTAATGGGCACAAATTTTCTGTCAGTGGAGAGGGTGAAGGTGATGCAACATACGGAAAACTTACCCTTAAATTTATTTGCACTACTGGAAAACTACCTGTTCCATGGCCAACACTTGTCACTACTTTCGGTTATGGTGTTCAATGCTTTGCGAGATACCCAGATCATATGAAACAGCATGACTTTTTCAAGAGTGCCATGCCCGAAGGTTATGTACAGGAAAGAACTATATTTTTCAAAGATGACGGGAACTACAAGACACGTGCTGAAGTCAAGTTTGAAGGTGATACCCTTGTTAATAGAATCGAGTTAAAAGGTATTGATTTTAAAGAAGATGGAAACATTCTTGGACACAAATTGGAATACAACTATAACTCACACAATGTATACATCATGGCAGACAAACAAAAGAATGGAATCAAAGTTAACTTCAAAATTAGACACAACATTGAAGATGGAAGCGTTCAACTAGCAGACCATTATCAACAAAATACTCCAATTGGCGATGGCCCTGTCCTTTTACCAGACAACCATTACCTGTCCACACAATCTGCCCTTTCGAAAGATCCCAACGAAAAGAGAGATCACATGGTCCTTCTTGAGTTTGTAACAGCTGCTGGGATTACACATGGCATGGATGAACTATACAAATAATAAAGGTCCAGGCATCAAATAAAACGAAAGGCTCAGTCGAAAGACTGGGCCTTTCGTTTTATCTGTTGTTTGTCGGTGAACGCTCTCTACTAGAGTCACACTGGCTCACCTTCGGGTGGGCCTTTCTGCGTTTATAGCTTatgtcttctactagtagcggccgctgcagtccggcaaaaaagggcaaggtgtcaccaccctgccctttttctttaaaaccgaaaagattacttcgcgttatgcaggcttcctcgctcactgactcgctgcgctcggtcgttcggctgcggcgagcggtatcagctcactcaaaggcggtaatacggttatccacagaatcaggggataacgcaggaaagaacatgtgagcaaaaggccagcaaaaggccaggaaccgtaaaaaggccgcgttgctggcgtttttccacaggctccgcccccctgacgagcatcacaaaaatcgacgctcaagtcagaggtggcgaaacccgacaggactataaagataccaggcgtttccccctggaagctccctcgtgcgctctcctgttccgaccctgccgcttaccggatacctgtccgcctttctcccttcgggaagcgtggcgctttctcatagctcacgctgtaggtatctcagttcggtgtaggtcgttcgctccaagctgggctgtgtgcacgaaccccccgttcagcccgaccgctgcgccttatccggtaactatcgtcttgagtccaacccggtaagacacgacttatcgccactggcagcagccactggtaacaggattagcagagcgaggtatgtaggcggtgctacagagttcttgaagtggtggcctaactacggctacactagaagaacagtatttggtatctgcgctctgctgaagccagttaccttcggaaaaagagttggtagctcttgatccggcaaacaaaccaccgctggtagcggtggtttttttgtttgcaagcagcagattacgcgcagaaaaaaaggatctcaagaagatcctttgatcttttctacggggtctgacgctcagtggaacgaaaactcacgttaagggattttggtcatgagattatcaaaaaggatcttcacctagatccttttaaattaaaaatgaagttttaaatcaatctaaagtatatatgagtaaacttggtctgacagctcgagtcccgtcaagtcagcgtaatgctctgccagtgttacaaccaattaaccaattctgattagaaaaactcatcgagcatcaaatgaaactgcaatttattcatatcaggattatcaataccatatttttgaaaaagccgtttctgtaatgaaggagaaaactcaccgaggcagttccataggatggcaagatcctggtatcggtctgcgattccgactcgtccaacatcaatacaacctattaatttcccctcgtcaaaaataaggttatcaagtgagaaatcaccatgagtgacgactgaatccggtgagaatggcaaaagcttatgcatttctttccagacttgttcaacaggccagccattacgctcgtcatcaaaatcactcgcatcaaccaaaccgttattcattcgtgattgcgcctgagcgagacgaaatacgcgatcgctgttaaaaggacaattacaaacaggaatcgaatgcaaccggcgcaggaacactgccagcgcatcaacaatattttcacctgaatcaggatattcttctaatacctggaatgctgttttcccggggatcgcagtggtgagtaaccatgcatcatcaggagtacggataaaatgcttgatggtcggaagaggcataaattccgtcagccagtttagtctgaccatctcatctgtaacatcattggcaacgctacctttgccatgtttcagaaacaactctggcgcatcgggcttcccatacaatcgatagattgtcgcacctgattgcccgacattatcgcgagcccatttatacccatataaatcagcatccatgttggaatttaatcgcggcctggagcaagacgtttcccgttgaatatggctcataacaccccttgtattactgtttatgtaagcagacagttttattgttcatgatgatatatttttatcttgtgcaatgtaacatcagagattttgagacacaacgtggctttgttgaataaatcgaacttttgctgagttgaaggatcagctcgagtgccacctgacgtctaagaaaccattattatcatgacattaacctataaaaataggcgtatcacgaggcagaatttcagataaaaaaaatccttagctttcgctaaggatgatttctggaattcgcggccgcttctagagactagtggaagacat + + standard_GFP_transformation_lvl1_seq + 1 + + + 1 + standard_GFP_transformation_lvl1_impl + + + + + + + E_coli_DH5alpha_impl + + 1 + + + + + 1 + E_coli_DH5alpha_with_standard_GFP_transformation_lvl1_impl + + + + 1 + + + 1 + + + pB0015_DE_impl + + + + DNA Assembly + + + pE0040_CD_impl + + 1 + + + + + + + + + + T4_Ligase + 1 + + + + + + + pB0034_BC_impl + 1 + + + + + + + + + + 1 + DVK_AE_impl + + + + + 1 + + + BsaI_enzyme + + + + + + + + + 1 + + assemble_ + + + standard_GFP_transformation_lvl1_assembly + + + + 1 + + pJ23100_AB_impl + + + + + + 1 + Transform E_coli_DH5alpha with standard_GFP_transformation_lvl1 + transform_standard_GFP_transformation_lvl1_1 + + + + 1 + + + + transform_standard_GFP_transformation_lvl1_1_plasmid_usage + + + + + + + + 1 + transform_standard_GFP_transformation_lvl1_1_chassis_usage + + + + + + + E_coli_DH5alpha + E_coli_DH5alpha + + 1 + + + 1 + E_coli_DH5alpha_with_standard_GFP_transformation_lvl1 + + + + + 1 + + E_coli_DH5alpha_with_standard_GFP_transformation_lvl1_plasmid_fc + + + + + + E_coli_DH5alpha_with_standard_GFP_transformation_lvl1_chassis_module + + 1 + + + + E_coli_DH5alpha transformed with standard_GFP_transformation_lvl1 + + + diff --git a/notebooks/results/buildcompiler_transformation_quickstart/transformation_summary.json b/notebooks/results/buildcompiler_transformation_quickstart/transformation_summary.json new file mode 100644 index 0000000..48efd2b --- /dev/null +++ b/notebooks/results/buildcompiler_transformation_quickstart/transformation_summary.json @@ -0,0 +1,49 @@ +{ + "stage": "transformation", + "inputs": [ + "standard_GFP_transformation_lvl1_A_E" + ], + "chassis": "E_coli_DH5alpha", + "sbol_artifacts": [ + { + "transformation_activity": "http://buildcompiler.org/transform_standard_GFP_transformation_lvl1_1/1", + "transformed_strain_module": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_transformation_lvl1/1", + "transformed_strain_implementation": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_transformation_lvl1_impl/1" + } + ], + "json_intermediate": { + "protocol": "chemical_transformation", + "version": "0.1", + "steps": [ + { + "step": 1, + "plasmid": "standard_GFP_transformation_lvl1", + "chassis": "E_coli_DH5alpha", + "mix_ul": { + "competent_cells": 50, + "assembly_product": 5 + }, + "heat_shock": { + "temperature_c": 42, + "duration_seconds": 45 + }, + "recovery": { + "medium": "SOC", + "volume_ul": 950, + "duration_min": 60 + } + } + ] + }, + "protocol_artifacts": { + "ot2_script": "TODO: adapter to protocol generator", + "human_instructions": [ + "Thaw competent cells on ice.", + "Combine assembly product with competent cells as specified.", + "Run heat shock and recovery according to generated parameters." + ], + "logs": [ + "Prepared transformation input for plasmid standard_GFP_transformation_lvl1 into chassis E_coli_DH5alpha." + ] + } +} \ No newline at end of file diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/assembly_lvl1_pudu_assembly_input.json b/notebooks/results/full_build_workflow_examples/lvl1_success 2/assembly_lvl1_pudu_assembly_input.json new file mode 100644 index 0000000..c687f78 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/assembly_lvl1_pudu_assembly_input.json @@ -0,0 +1,13 @@ +[ + { + "Product": "http://buildcompiler.org/standard_GFP_full_build/1", + "Backbone": "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/DVK_AE/1", + "PartsList": [ + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pJ23100_AB/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0034_BC/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pE0040_CD/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0015_DE/1" + ], + "Restriction Enzyme": "https://synbiohub.org/user/Gon/Enzyme_Implementations/BsaI/1" + } +] diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/assembly_lvl1_pudu_assembly_protocol.py b/notebooks/results/full_build_workflow_examples/lvl1_success 2/assembly_lvl1_pudu_assembly_protocol.py new file mode 100644 index 0000000..73b1757 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/assembly_lvl1_pudu_assembly_protocol.py @@ -0,0 +1,26 @@ +from pudu.assembly import SBOLLoopAssembly +from opentrons import protocol_api + +assembly_data = [ + { + "Product": "http://buildcompiler.org/standard_GFP_full_build/1", + "Backbone": "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/DVK_AE/1", + "PartsList": [ + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pJ23100_AB/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0034_BC/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pE0040_CD/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0015_DE/1" + ], + "Restriction Enzyme": "https://synbiohub.org/user/Gon/Enzyme_Implementations/BsaI/1" + } +] + +metadata = { + 'protocolName': 'BuildCompiler assembly_lvl1 Assembly', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = SBOLLoopAssembly(assembly_data=assembly_data) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/full_build_manifest.json b/notebooks/results/full_build_workflow_examples/lvl1_success 2/full_build_manifest.json new file mode 100644 index 0000000..3b85fe6 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/full_build_manifest.json @@ -0,0 +1,156 @@ +{ + "results_dir": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success", + "domestication": { + "successful": [], + "failed": [] + }, + "assembly_lvl1": { + "successful": [ + { + "design": "standard_GFP", + "products": [ + "http://buildcompiler.org/standard_GFP_full_build/1" + ] + } + ], + "failed": [] + }, + "assembly_lvl2": { + "successful": [], + "failed": [] + }, + "transformation": { + "successful": [ + { + "stage_label": "standard_GFP", + "products": [ + "http://buildcompiler.org/standard_GFP_full_build/1" + ], + "result": { + "stage": "transformation", + "inputs": [ + "standard_GFP_full_build_A_E" + ], + "chassis": "E_coli_DH5alpha", + "sbol_artifacts": [ + { + "transformation_activity": "http://buildcompiler.org/transform_standard_GFP_full_build_1/1", + "transformed_strain_module": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "transformed_strain_implementation": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1" + } + ], + "json_intermediate": { + "protocol": "chemical_transformation", + "version": "0.1", + "steps": [ + { + "step": 1, + "plasmid": "standard_GFP_full_build", + "chassis": "E_coli_DH5alpha", + "mix_ul": { + "competent_cells": 50, + "assembly_product": 5 + }, + "heat_shock": { + "temperature_c": 42, + "duration_seconds": 45 + }, + "recovery": { + "medium": "SOC", + "volume_ul": 950, + "duration_min": 60 + } + } + ] + }, + "protocol_artifacts": { + "ot2_script": "TODO: adapter to protocol generator", + "human_instructions": [ + "Thaw competent cells on ice.", + "Combine assembly product with competent cells as specified.", + "Run heat shock and recovery according to generated parameters." + ], + "logs": [ + "Prepared transformation input for plasmid standard_GFP_full_build into chassis E_coli_DH5alpha." + ] + } + } + } + ], + "failed": [] + }, + "plating": { + "successful": [ + { + "stage_label": "standard_GFP", + "result": { + "stage": "plating", + "protocol_type": "manual", + "plate": { + "plate_implementation": "solid_96_well_plate", + "plate_map": { + "A1": "1_plated_A1_impl" + } + }, + "metadata": { + "plate_rows": [ + { + "well": "A1", + "source_transformed_strain_implementation": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1", + "strain_module": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "plated_strain_implementation": "1_plated_A1_impl", + "strain_display_name": "1_plated_A1_impl" + } + ], + "layout_dataframe_columns": [ + "well", + "source_transformed_strain_implementation", + "strain_module", + "plated_strain_implementation", + "strain_display_name" + ] + }, + "json_intermediate": { + "plating_data": { + "bacterium_locations": { + "A1": "1_plated_A1_impl" + } + }, + "advanced_params": {} + }, + "protocol_artifacts": { + "plate_map_json": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.json", + "plate_map_csv": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.csv", + "plate_layout_dataframe_csv": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_layout_dataframe.csv", + "logs": [], + "pudu": { + "runner_script": "https://github.com/MyersResearchGroup/PUDU/blob/main/scripts/run_sbol2plating_with_params.py", + "mode": "manual", + "advanced_params": {} + }, + "manual_protocol_markdown": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/manual_plating_protocol.md" + } + } + } + ], + "failed": [] + }, + "skipped": [ + { + "stage": "assembly_lvl2", + "reason": "no level-2 design provided" + } + ], + "artifacts": [ + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_protocol.py", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_pudu_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_plasmid_locations.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_transformation_protocol.py", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/plating_pudu_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_plating_protocol.py" + ], + "manifest_path": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/full_build_manifest.json", + "zip_path": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success.zip", + "artifact_zip": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success.zip" +} diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/plating_pudu_input.json b/notebooks/results/full_build_workflow_examples/lvl1_success 2/plating_pudu_input.json new file mode 100644 index 0000000..9812604 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/plating_pudu_input.json @@ -0,0 +1,5 @@ +{ + "bacterium_locations": { + "A1": "1_plated_A1_impl" + } +} diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/pudu_plating_protocol.py b/notebooks/results/full_build_workflow_examples/lvl1_success 2/pudu_plating_protocol.py new file mode 100644 index 0000000..e8c41f8 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/pudu_plating_protocol.py @@ -0,0 +1,18 @@ +from pudu.plating import Plating +from opentrons import protocol_api + +plating_data = { + "bacterium_locations": { + "A1": "1_plated_A1_impl" + } +} + +metadata = { + 'protocolName': 'BuildCompiler Plating', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = Plating(plating_data=plating_data) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/pudu_transformation_protocol.py b/notebooks/results/full_build_workflow_examples/lvl1_success 2/pudu_transformation_protocol.py new file mode 100644 index 0000000..42392d7 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/pudu_transformation_protocol.py @@ -0,0 +1,31 @@ +from pudu.transformation import HeatShockTransformation +from opentrons import protocol_api + +transformation_data = [ + { + "Strain": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/standard_GFP_full_build/1" + ] + } +] + +plasmid_locations = { + "http://buildcompiler.org/standard_GFP_full_build/1": [ + "A1" + ] +} + +metadata = { + 'protocolName': 'BuildCompiler Transformation', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = HeatShockTransformation( + transformation_data=transformation_data, + plasmid_locations=plasmid_locations, + ) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/manual_plating_protocol.md b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/manual_plating_protocol.md new file mode 100644 index 0000000..bc86255 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/manual_plating_protocol.md @@ -0,0 +1,22 @@ +# BuildCompiler Plating Protocol + +## Plate +- Plate ID: `solid_96_well_plate` +- Protocol type: `manual` + +## Input transformed strains +- 1_plated_A1_impl + +## Parameters +- (none) + +## 96-well plate map +| Well | Source transformed strain implementation | Plated strain implementation | Strain module | +|---|---|---|---| +| A1 | http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1 | 1_plated_A1_impl | http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1 | + +## Steps +1. Prepare one sterile solid-media 96-well plate. +2. Label the plate with the plate ID and date. +3. Transfer each transformed strain to the destination well shown in the map. +4. Incubate according to lab defaults or parameters above. diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_layout_dataframe.csv b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_layout_dataframe.csv new file mode 100644 index 0000000..0053e37 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_layout_dataframe.csv @@ -0,0 +1,2 @@ +well,source_transformed_strain_implementation,strain_module,plated_strain_implementation,strain_display_name +A1,http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1,http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1,1_plated_A1_impl,1_plated_A1_impl diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_map.csv b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_map.csv new file mode 100644 index 0000000..0053e37 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_map.csv @@ -0,0 +1,2 @@ +well,source_transformed_strain_implementation,strain_module,plated_strain_implementation,strain_display_name +A1,http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1,http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1,1_plated_A1_impl,1_plated_A1_impl diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_map.json b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_map.json new file mode 100644 index 0000000..b8c290a --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plate_map.json @@ -0,0 +1,13 @@ +{ + "plate_implementation": "solid_96_well_plate", + "protocol_type": "manual", + "well_map": [ + { + "well": "A1", + "source_transformed_strain_implementation": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1", + "strain_module": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "plated_strain_implementation": "1_plated_A1_impl", + "strain_display_name": "1_plated_A1_impl" + } + ] +} \ No newline at end of file diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plating_input.json b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plating_input.json new file mode 100644 index 0000000..ff85937 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/standard_GFP_plating/plating_input.json @@ -0,0 +1,5 @@ +{ + "bacterium_locations": { + "A1": "1_plated_A1_impl" + } +} \ No newline at end of file diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/transformation_plasmid_locations.json b/notebooks/results/full_build_workflow_examples/lvl1_success 2/transformation_plasmid_locations.json new file mode 100644 index 0000000..e722f2c --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/transformation_plasmid_locations.json @@ -0,0 +1,5 @@ +{ + "http://buildcompiler.org/standard_GFP_full_build/1": [ + "A1" + ] +} diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success 2/transformation_pudu_input.json b/notebooks/results/full_build_workflow_examples/lvl1_success 2/transformation_pudu_input.json new file mode 100644 index 0000000..ebff170 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success 2/transformation_pudu_input.json @@ -0,0 +1,9 @@ +[ + { + "Strain": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/standard_GFP_full_build/1" + ] + } +] diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success.zip b/notebooks/results/full_build_workflow_examples/lvl1_success.zip new file mode 100644 index 0000000..71614b3 Binary files /dev/null and b/notebooks/results/full_build_workflow_examples/lvl1_success.zip differ diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_input.json b/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_input.json new file mode 100644 index 0000000..c687f78 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_input.json @@ -0,0 +1,13 @@ +[ + { + "Product": "http://buildcompiler.org/standard_GFP_full_build/1", + "Backbone": "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/DVK_AE/1", + "PartsList": [ + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pJ23100_AB/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0034_BC/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pE0040_CD/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0015_DE/1" + ], + "Restriction Enzyme": "https://synbiohub.org/user/Gon/Enzyme_Implementations/BsaI/1" + } +] diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_protocol.py b/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_protocol.py new file mode 100644 index 0000000..73b1757 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_protocol.py @@ -0,0 +1,26 @@ +from pudu.assembly import SBOLLoopAssembly +from opentrons import protocol_api + +assembly_data = [ + { + "Product": "http://buildcompiler.org/standard_GFP_full_build/1", + "Backbone": "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/DVK_AE/1", + "PartsList": [ + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pJ23100_AB/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0034_BC/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pE0040_CD/1", + "https://synbiohub.org/user/Gon/CIDARMoCloPlasmidsKit/pB0015_DE/1" + ], + "Restriction Enzyme": "https://synbiohub.org/user/Gon/Enzyme_Implementations/BsaI/1" + } +] + +metadata = { + 'protocolName': 'BuildCompiler assembly_lvl1 Assembly', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = SBOLLoopAssembly(assembly_data=assembly_data) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/full_build_manifest.json b/notebooks/results/full_build_workflow_examples/lvl1_success/full_build_manifest.json new file mode 100644 index 0000000..3b85fe6 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/full_build_manifest.json @@ -0,0 +1,156 @@ +{ + "results_dir": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success", + "domestication": { + "successful": [], + "failed": [] + }, + "assembly_lvl1": { + "successful": [ + { + "design": "standard_GFP", + "products": [ + "http://buildcompiler.org/standard_GFP_full_build/1" + ] + } + ], + "failed": [] + }, + "assembly_lvl2": { + "successful": [], + "failed": [] + }, + "transformation": { + "successful": [ + { + "stage_label": "standard_GFP", + "products": [ + "http://buildcompiler.org/standard_GFP_full_build/1" + ], + "result": { + "stage": "transformation", + "inputs": [ + "standard_GFP_full_build_A_E" + ], + "chassis": "E_coli_DH5alpha", + "sbol_artifacts": [ + { + "transformation_activity": "http://buildcompiler.org/transform_standard_GFP_full_build_1/1", + "transformed_strain_module": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "transformed_strain_implementation": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1" + } + ], + "json_intermediate": { + "protocol": "chemical_transformation", + "version": "0.1", + "steps": [ + { + "step": 1, + "plasmid": "standard_GFP_full_build", + "chassis": "E_coli_DH5alpha", + "mix_ul": { + "competent_cells": 50, + "assembly_product": 5 + }, + "heat_shock": { + "temperature_c": 42, + "duration_seconds": 45 + }, + "recovery": { + "medium": "SOC", + "volume_ul": 950, + "duration_min": 60 + } + } + ] + }, + "protocol_artifacts": { + "ot2_script": "TODO: adapter to protocol generator", + "human_instructions": [ + "Thaw competent cells on ice.", + "Combine assembly product with competent cells as specified.", + "Run heat shock and recovery according to generated parameters." + ], + "logs": [ + "Prepared transformation input for plasmid standard_GFP_full_build into chassis E_coli_DH5alpha." + ] + } + } + } + ], + "failed": [] + }, + "plating": { + "successful": [ + { + "stage_label": "standard_GFP", + "result": { + "stage": "plating", + "protocol_type": "manual", + "plate": { + "plate_implementation": "solid_96_well_plate", + "plate_map": { + "A1": "1_plated_A1_impl" + } + }, + "metadata": { + "plate_rows": [ + { + "well": "A1", + "source_transformed_strain_implementation": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1", + "strain_module": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "plated_strain_implementation": "1_plated_A1_impl", + "strain_display_name": "1_plated_A1_impl" + } + ], + "layout_dataframe_columns": [ + "well", + "source_transformed_strain_implementation", + "strain_module", + "plated_strain_implementation", + "strain_display_name" + ] + }, + "json_intermediate": { + "plating_data": { + "bacterium_locations": { + "A1": "1_plated_A1_impl" + } + }, + "advanced_params": {} + }, + "protocol_artifacts": { + "plate_map_json": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.json", + "plate_map_csv": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.csv", + "plate_layout_dataframe_csv": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_layout_dataframe.csv", + "logs": [], + "pudu": { + "runner_script": "https://github.com/MyersResearchGroup/PUDU/blob/main/scripts/run_sbol2plating_with_params.py", + "mode": "manual", + "advanced_params": {} + }, + "manual_protocol_markdown": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/manual_plating_protocol.md" + } + } + } + ], + "failed": [] + }, + "skipped": [ + { + "stage": "assembly_lvl2", + "reason": "no level-2 design provided" + } + ], + "artifacts": [ + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/assembly_lvl1_pudu_assembly_protocol.py", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_pudu_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_plasmid_locations.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_transformation_protocol.py", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/plating_pudu_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_plating_protocol.py" + ], + "manifest_path": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success/full_build_manifest.json", + "zip_path": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success.zip", + "artifact_zip": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl1_success.zip" +} diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/plating_pudu_input.json b/notebooks/results/full_build_workflow_examples/lvl1_success/plating_pudu_input.json new file mode 100644 index 0000000..9812604 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/plating_pudu_input.json @@ -0,0 +1,5 @@ +{ + "bacterium_locations": { + "A1": "1_plated_A1_impl" + } +} diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_plating_protocol.py b/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_plating_protocol.py new file mode 100644 index 0000000..e8c41f8 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_plating_protocol.py @@ -0,0 +1,18 @@ +from pudu.plating import Plating +from opentrons import protocol_api + +plating_data = { + "bacterium_locations": { + "A1": "1_plated_A1_impl" + } +} + +metadata = { + 'protocolName': 'BuildCompiler Plating', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = Plating(plating_data=plating_data) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_transformation_protocol.py b/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_transformation_protocol.py new file mode 100644 index 0000000..42392d7 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/pudu_transformation_protocol.py @@ -0,0 +1,31 @@ +from pudu.transformation import HeatShockTransformation +from opentrons import protocol_api + +transformation_data = [ + { + "Strain": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/standard_GFP_full_build/1" + ] + } +] + +plasmid_locations = { + "http://buildcompiler.org/standard_GFP_full_build/1": [ + "A1" + ] +} + +metadata = { + 'protocolName': 'BuildCompiler Transformation', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = HeatShockTransformation( + transformation_data=transformation_data, + plasmid_locations=plasmid_locations, + ) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/manual_plating_protocol.md b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/manual_plating_protocol.md new file mode 100644 index 0000000..bc86255 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/manual_plating_protocol.md @@ -0,0 +1,22 @@ +# BuildCompiler Plating Protocol + +## Plate +- Plate ID: `solid_96_well_plate` +- Protocol type: `manual` + +## Input transformed strains +- 1_plated_A1_impl + +## Parameters +- (none) + +## 96-well plate map +| Well | Source transformed strain implementation | Plated strain implementation | Strain module | +|---|---|---|---| +| A1 | http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1 | 1_plated_A1_impl | http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1 | + +## Steps +1. Prepare one sterile solid-media 96-well plate. +2. Label the plate with the plate ID and date. +3. Transfer each transformed strain to the destination well shown in the map. +4. Incubate according to lab defaults or parameters above. diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_layout_dataframe.csv b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_layout_dataframe.csv new file mode 100644 index 0000000..0053e37 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_layout_dataframe.csv @@ -0,0 +1,2 @@ +well,source_transformed_strain_implementation,strain_module,plated_strain_implementation,strain_display_name +A1,http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1,http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1,1_plated_A1_impl,1_plated_A1_impl diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.csv b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.csv new file mode 100644 index 0000000..0053e37 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.csv @@ -0,0 +1,2 @@ +well,source_transformed_strain_implementation,strain_module,plated_strain_implementation,strain_display_name +A1,http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1,http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1,1_plated_A1_impl,1_plated_A1_impl diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.json b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.json new file mode 100644 index 0000000..b8c290a --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plate_map.json @@ -0,0 +1,13 @@ +{ + "plate_implementation": "solid_96_well_plate", + "protocol_type": "manual", + "well_map": [ + { + "well": "A1", + "source_transformed_strain_implementation": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build_impl/1", + "strain_module": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "plated_strain_implementation": "1_plated_A1_impl", + "strain_display_name": "1_plated_A1_impl" + } + ] +} \ No newline at end of file diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plating_input.json b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plating_input.json new file mode 100644 index 0000000..ff85937 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/standard_GFP_plating/plating_input.json @@ -0,0 +1,5 @@ +{ + "bacterium_locations": { + "A1": "1_plated_A1_impl" + } +} \ No newline at end of file diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_plasmid_locations.json b/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_plasmid_locations.json new file mode 100644 index 0000000..e722f2c --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_plasmid_locations.json @@ -0,0 +1,5 @@ +{ + "http://buildcompiler.org/standard_GFP_full_build/1": [ + "A1" + ] +} diff --git a/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_pudu_input.json b/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_pudu_input.json new file mode 100644 index 0000000..ebff170 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl1_success/transformation_pudu_input.json @@ -0,0 +1,9 @@ +[ + { + "Strain": "http://buildcompiler.org/E_coli_DH5alpha_with_standard_GFP_full_build/1", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/standard_GFP_full_build/1" + ] + } +] diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages.zip b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages.zip new file mode 100644 index 0000000..828d424 Binary files /dev/null and b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages.zip differ diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl1_pudu_assembly_input.json b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl1_pudu_assembly_input.json new file mode 100644 index 0000000..6a7484a --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl1_pudu_assembly_input.json @@ -0,0 +1,10 @@ +[ + { + "Product": "http://buildcompiler.org/assembled_demo_tu/1", + "Backbone": "demo_lvl1_backbone", + "PartsList": [ + "http://buildcompiler.org/domesticated_missing_promoter/1" + ], + "Restriction Enzyme": "BsaI" + } +] diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl1_pudu_assembly_protocol.py b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl1_pudu_assembly_protocol.py new file mode 100644 index 0000000..0f4ed2b --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl1_pudu_assembly_protocol.py @@ -0,0 +1,23 @@ +from pudu.assembly import SBOLLoopAssembly +from opentrons import protocol_api + +assembly_data = [ + { + "Product": "http://buildcompiler.org/assembled_demo_tu/1", + "Backbone": "demo_lvl1_backbone", + "PartsList": [ + "http://buildcompiler.org/domesticated_missing_promoter/1" + ], + "Restriction Enzyme": "BsaI" + } +] + +metadata = { + 'protocolName': 'BuildCompiler assembly_lvl1 Assembly', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = SBOLLoopAssembly(assembly_data=assembly_data) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl2_pudu_assembly_input.json b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl2_pudu_assembly_input.json new file mode 100644 index 0000000..789a52b --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl2_pudu_assembly_input.json @@ -0,0 +1,10 @@ +[ + { + "Product": "http://buildcompiler.org/assembled_demo_lvl2/1", + "Backbone": "demo_lvl2_backbone", + "PartsList": [ + "http://buildcompiler.org/assembled_demo_tu/1" + ], + "Restriction Enzyme": "BbsI" + } +] diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl2_pudu_assembly_protocol.py b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl2_pudu_assembly_protocol.py new file mode 100644 index 0000000..62f7c9f --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl2_pudu_assembly_protocol.py @@ -0,0 +1,23 @@ +from pudu.assembly import SBOLLoopAssembly +from opentrons import protocol_api + +assembly_data = [ + { + "Product": "http://buildcompiler.org/assembled_demo_lvl2/1", + "Backbone": "demo_lvl2_backbone", + "PartsList": [ + "http://buildcompiler.org/assembled_demo_tu/1" + ], + "Restriction Enzyme": "BbsI" + } +] + +metadata = { + 'protocolName': 'BuildCompiler assembly_lvl2 Assembly', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = SBOLLoopAssembly(assembly_data=assembly_data) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/domestication_pudu_assembly_input.json b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/domestication_pudu_assembly_input.json new file mode 100644 index 0000000..847b17e --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/domestication_pudu_assembly_input.json @@ -0,0 +1,10 @@ +[ + { + "Product": "http://buildcompiler.org/domesticated_missing_promoter/1", + "Backbone": "demo_domestication_backbone", + "PartsList": [ + "http://buildcompiler.org/missing_promoter/1" + ], + "Restriction Enzyme": "BsaI" + } +] diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/domestication_pudu_assembly_protocol.py b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/domestication_pudu_assembly_protocol.py new file mode 100644 index 0000000..92d915e --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/domestication_pudu_assembly_protocol.py @@ -0,0 +1,23 @@ +from pudu.assembly import SBOLLoopAssembly +from opentrons import protocol_api + +assembly_data = [ + { + "Product": "http://buildcompiler.org/domesticated_missing_promoter/1", + "Backbone": "demo_domestication_backbone", + "PartsList": [ + "http://buildcompiler.org/missing_promoter/1" + ], + "Restriction Enzyme": "BsaI" + } +] + +metadata = { + 'protocolName': 'BuildCompiler domestication Assembly', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = SBOLLoopAssembly(assembly_data=assembly_data) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/full_build_manifest.json b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/full_build_manifest.json new file mode 100644 index 0000000..12015ad --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/full_build_manifest.json @@ -0,0 +1,164 @@ +{ + "results_dir": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages", + "domestication": { + "successful": [ + { + "parts": [ + "missing_promoter" + ], + "products": [ + "http://buildcompiler.org/domesticated_missing_promoter/1" + ] + } + ], + "failed": [] + }, + "assembly_lvl1": { + "successful": [ + { + "design": "demo_tu", + "products": [ + "http://buildcompiler.org/assembled_demo_tu/1" + ] + } + ], + "failed": [ + { + "design": "demo_tu", + "error": "level-1 input is missing a domesticated part" + } + ] + }, + "assembly_lvl2": { + "successful": [ + { + "design": "demo_lvl2_design", + "products": [ + "http://buildcompiler.org/assembled_demo_lvl2/1" + ], + "after_recovery": true + } + ], + "failed": [ + { + "design": "demo_lvl2_design", + "error": "level-2 input is missing level-1 regions", + "recovery": "attempting level-1 assembly and domestication" + } + ] + }, + "transformation": { + "successful": [ + { + "stage_label": "domestication", + "products": [ + "http://buildcompiler.org/domesticated_missing_promoter/1" + ], + "result": { + "stage": "transformation", + "chassis": "E_coli_DH5alpha", + "sbol_artifacts": [ + { + "transformed_strain_module": "http://buildcompiler.org/domesticated_missing_promoter/1_strain", + "transformed_strain_implementation": "http://buildcompiler.org/domesticated_missing_promoter/1_strain_impl" + } + ] + } + }, + { + "stage_label": "full_build_lvl2_1_lvl1", + "products": [ + "http://buildcompiler.org/assembled_demo_tu/1" + ], + "result": { + "stage": "transformation", + "chassis": "E_coli_DH5alpha", + "sbol_artifacts": [ + { + "transformed_strain_module": "http://buildcompiler.org/assembled_demo_tu/1_strain", + "transformed_strain_implementation": "http://buildcompiler.org/assembled_demo_tu/1_strain_impl" + } + ] + } + }, + { + "stage_label": "lvl2_1_final", + "products": [ + "http://buildcompiler.org/assembled_demo_lvl2/1" + ], + "result": { + "stage": "transformation", + "chassis": "E_coli_DH5alpha", + "sbol_artifacts": [ + { + "transformed_strain_module": "http://buildcompiler.org/assembled_demo_lvl2/1_strain", + "transformed_strain_implementation": "http://buildcompiler.org/assembled_demo_lvl2/1_strain_impl" + } + ] + } + } + ], + "failed": [] + }, + "plating": { + "successful": [ + { + "stage_label": "domestication", + "result": { + "stage": "plating", + "json_intermediate": { + "plating_data": { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + } + } + } + }, + { + "stage_label": "full_build_lvl2_1_lvl1", + "result": { + "stage": "plating", + "json_intermediate": { + "plating_data": { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + } + } + } + }, + { + "stage_label": "lvl2_1_final", + "result": { + "stage": "plating", + "json_intermediate": { + "plating_data": { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + } + } + } + } + ], + "failed": [] + }, + "skipped": [], + "artifacts": [ + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl1_pudu_assembly_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl1_pudu_assembly_protocol.py", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl2_pudu_assembly_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/assembly_lvl2_pudu_assembly_protocol.py", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/domestication_pudu_assembly_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/domestication_pudu_assembly_protocol.py", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/transformation_pudu_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/transformation_plasmid_locations.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/pudu_transformation_protocol.py", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/plating_pudu_input.json", + "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/pudu_plating_protocol.py" + ], + "manifest_path": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/full_build_manifest.json", + "zip_path": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages.zip", + "artifact_zip": "/Users/gonzalovidal/Documents/GitHub/BuildCompiler/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages.zip" +} diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/plating_pudu_input.json b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/plating_pudu_input.json new file mode 100644 index 0000000..a5114c5 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/plating_pudu_input.json @@ -0,0 +1,19 @@ +{ + "batches": [ + { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + }, + { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + }, + { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + } + ] +} diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/pudu_plating_protocol.py b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/pudu_plating_protocol.py new file mode 100644 index 0000000..f5ed7f5 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/pudu_plating_protocol.py @@ -0,0 +1,32 @@ +from pudu.plating import Plating +from opentrons import protocol_api + +plating_data = { + "batches": [ + { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + }, + { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + }, + { + "bacterium_locations": { + "A1": "demo_transformed_strain" + } + } + ] +} + +metadata = { + 'protocolName': 'BuildCompiler Plating', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = Plating(plating_data=plating_data) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/pudu_transformation_protocol.py b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/pudu_transformation_protocol.py new file mode 100644 index 0000000..6a1d04d --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/pudu_transformation_protocol.py @@ -0,0 +1,51 @@ +from pudu.transformation import HeatShockTransformation +from opentrons import protocol_api + +transformation_data = [ + { + "Strain": "http://buildcompiler.org/domesticated_missing_promoter/1_strain", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/domesticated_missing_promoter/1" + ] + }, + { + "Strain": "http://buildcompiler.org/assembled_demo_tu/1_strain", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/assembled_demo_tu/1" + ] + }, + { + "Strain": "http://buildcompiler.org/assembled_demo_lvl2/1_strain", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/assembled_demo_lvl2/1" + ] + } +] + +plasmid_locations = { + "http://buildcompiler.org/domesticated_missing_promoter/1": [ + "A1" + ], + "http://buildcompiler.org/assembled_demo_tu/1": [ + "B1" + ], + "http://buildcompiler.org/assembled_demo_lvl2/1": [ + "C1" + ] +} + +metadata = { + 'protocolName': 'BuildCompiler Transformation', + 'author': 'BuildCompiler', + 'apiLevel': '2.21', +} + +def run(protocol: protocol_api.ProtocolContext): + protocol_instance = HeatShockTransformation( + transformation_data=transformation_data, + plasmid_locations=plasmid_locations, + ) + protocol_instance.run(protocol) diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/transformation_plasmid_locations.json b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/transformation_plasmid_locations.json new file mode 100644 index 0000000..849779b --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/transformation_plasmid_locations.json @@ -0,0 +1,11 @@ +{ + "http://buildcompiler.org/domesticated_missing_promoter/1": [ + "A1" + ], + "http://buildcompiler.org/assembled_demo_tu/1": [ + "B1" + ], + "http://buildcompiler.org/assembled_demo_lvl2/1": [ + "C1" + ] +} diff --git a/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/transformation_pudu_input.json b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/transformation_pudu_input.json new file mode 100644 index 0000000..9ed14f5 --- /dev/null +++ b/notebooks/results/full_build_workflow_examples/lvl2_triggers_all_stages/transformation_pudu_input.json @@ -0,0 +1,23 @@ +[ + { + "Strain": "http://buildcompiler.org/domesticated_missing_promoter/1_strain", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/domesticated_missing_promoter/1" + ] + }, + { + "Strain": "http://buildcompiler.org/assembled_demo_tu/1_strain", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/assembled_demo_tu/1" + ] + }, + { + "Strain": "http://buildcompiler.org/assembled_demo_lvl2/1_strain", + "Chassis": "E_coli_DH5alpha", + "Plasmids": [ + "http://buildcompiler.org/assembled_demo_lvl2/1" + ] + } +] diff --git a/src/buildcompiler/adapters/pudu/__init__.py b/src/buildcompiler/adapters/pudu/__init__.py index eb22839..242d8f4 100644 --- a/src/buildcompiler/adapters/pudu/__init__.py +++ b/src/buildcompiler/adapters/pudu/__init__.py @@ -11,6 +11,7 @@ ) from .plating_json import plating_to_pudu_json from .transformation_json import ( + plasmid_locations_to_pudu_json, transformation_to_pudu_json, transformations_to_pudu_json, ) @@ -23,6 +24,7 @@ "legacy_assembly_route_to_pudu_json", "legacy_assembly_routes_to_pudu_json", "write_assembly_pudu_input_json", + "plasmid_locations_to_pudu_json", "transformation_to_pudu_json", "transformations_to_pudu_json", "plating_to_pudu_json", diff --git a/src/buildcompiler/adapters/pudu/plating_json.py b/src/buildcompiler/adapters/pudu/plating_json.py index 3648e20..a6656cf 100644 --- a/src/buildcompiler/adapters/pudu/plating_json.py +++ b/src/buildcompiler/adapters/pudu/plating_json.py @@ -7,16 +7,21 @@ def plating_to_pudu_json( *, - bacterium_locations: Mapping[str, str], + bacterium_locations: Mapping[str, str | list[str]], advanced_parameters: Mapping[str, object] | None = None, ) -> dict[str, object]: - """Adapt plating records into deterministic legacy-compatible PUDU JSON keys.""" + """Adapt plating records into PUDU's plating input JSON. + + PUDU expects thermocycler source wells as keys and transformed construct + identifiers as values, wrapped under ``bacterium_locations``. + """ stable_locations = OrderedDict( sorted(bacterium_locations.items(), key=lambda kv: kv[0]) ) payload: dict[str, Any] = { "bacterium_locations": dict(stable_locations), - "advanced_parameters": dict(advanced_parameters or {}), } + if advanced_parameters: + payload.update(dict(advanced_parameters)) return payload diff --git a/src/buildcompiler/adapters/pudu/transformation_json.py b/src/buildcompiler/adapters/pudu/transformation_json.py index 207a561..4385da9 100644 --- a/src/buildcompiler/adapters/pudu/transformation_json.py +++ b/src/buildcompiler/adapters/pudu/transformation_json.py @@ -5,6 +5,11 @@ from buildcompiler.domain import IndexedPlasmid +PUDU_96_WELL_ORDER = tuple( + f"{row}{column}" for column in range(1, 13) for row in "ABCDEFGH" +) + + def _stable_identifier(identity: str, display_id: str | None) -> str: return identity or display_id or "" @@ -51,3 +56,31 @@ def transformations_to_pudu_json( strict=True, ) ] + + +def plasmid_locations_to_pudu_json( + plasmids: Sequence[IndexedPlasmid | str], + *, + wells: Sequence[str] | None = None, +) -> dict[str, list[str]]: + """Create PUDU's assembly-output plasmid location map. + + PUDU's transformation protocol optionally consumes the + ``transformation_input.json`` emitted by its assembly simulation. The shape + is ``{"plasmid_uri": ["A1"]}``, where each value is a list because one + plasmid may be available in multiple source wells. + """ + + if wells is None: + wells = PUDU_96_WELL_ORDER[: len(plasmids)] + if len(plasmids) != len(wells): + raise ValueError("plasmids and wells must have the same length.") + + locations: dict[str, list[str]] = {} + for plasmid, well in zip(plasmids, wells, strict=True): + plasmid_id = _plasmid_identifier(plasmid) + if not plasmid_id: + raise ValueError("plasmid identity cannot be empty.") + locations.setdefault(plasmid_id, []).append(well) + + return locations diff --git a/src/buildcompiler/buildcompiler.py b/src/buildcompiler/buildcompiler.py index d450f4a..e49417d 100644 --- a/src/buildcompiler/buildcompiler.py +++ b/src/buildcompiler/buildcompiler.py @@ -4,6 +4,7 @@ import re import shutil import warnings +import zipfile from typing import Any, List, Dict, Tuple import urllib.parse import csv @@ -32,7 +33,13 @@ write_plate_map_json, write_plating_protocol_script, ) -from .adapters.pudu import legacy_assembly_routes_to_pudu_json +from .adapters.pudu import ( + legacy_assembly_routes_to_pudu_json, + plasmid_locations_to_pudu_json, + plating_to_pudu_json, + transformations_to_pudu_json, + write_assembly_pudu_input_json, +) from .constants import ( AMP, ENGINEERED_REGION, @@ -80,6 +87,7 @@ def __init__( self.restriction_enzyme_implementations = [] self.ligase_implementations = [] self.last_assembly_pudu_json = [] + self.last_assembly_pudu_json_by_stage = {} self.BsaI_impl = None self.BbsI_impl = None self.T4_ligase_impl = None @@ -103,6 +111,7 @@ def from_local_documents( compiler.restriction_enzyme_implementations = [] compiler.ligase_implementations = [] compiler.last_assembly_pudu_json = [] + compiler.last_assembly_pudu_json_by_stage = {} compiler.BsaI_impl = None compiler.BbsI_impl = None compiler.T4_ligase_impl = None @@ -294,6 +303,7 @@ def _remove_internal_bsai_sites(sequence: str) -> tuple[str, int]: dsDNAs = [] domesticated_parts = [] + pudu_payloads = [] for part in parts: part_role = next( @@ -394,7 +404,21 @@ def _remove_internal_bsai_sites(sequence: str) -> tuple[str, int]: assembly_products, assembly_doc = assembly.run() product_definition = assembly_products[0].plasmid_definition domesticated_parts.append(product_definition) + pudu_payloads.extend( + legacy_assembly_routes_to_pudu_json( + product_plasmids=assembly_products, + part_plasmid_routes=[ + [insert_plasmid] for _ in range(len(assembly_products)) + ], + backbones=[backbone for _ in range(len(assembly_products))], + restriction_enzymes=[ + self.BsaI_impl for _ in range(len(assembly_products)) + ], + ) + ) + self.last_assembly_pudu_json = pudu_payloads + self.last_assembly_pudu_json_by_stage["domestication"] = list(pudu_payloads) return domesticated_parts def assembly_lvl1( @@ -429,8 +453,8 @@ def assembly_lvl1( enumerated_part_lists = enumerate_design_variants(combinatorial_part_dict) - for i, list in enumerate(enumerated_part_lists): - plasmid_dict = self._construct_plasmid_dict(list, AMP) + for i, part_list in enumerate(enumerated_part_lists): + plasmid_dict = self._construct_plasmid_dict(part_list, AMP) if isinstance(backbone, dict): raise ValueError( @@ -567,6 +591,7 @@ def assembly_lvl1( assembly_dict[abstract_design.identity] = composite_plasmids self.last_assembly_pudu_json = pudu_payloads + self.last_assembly_pudu_json_by_stage["assembly_lvl1"] = list(pudu_payloads) return assembly_dict, final_doc def assembly_lvl2( @@ -610,6 +635,7 @@ def assembly_lvl2( composite_plasmid_dict, final_doc = self.assembly_lvl1( TUs, backbone=backbone_dict, product_name=f"{TU.displayId}_plas" ) + lvl1_pudu_payloads = list(self.last_assembly_pudu_json) for key, composites in composite_plasmid_dict.items(): simplified_representation, new_defs = self._encapsulate_TU(composites[0]) @@ -655,12 +681,15 @@ def assembly_lvl2( ) lvl2_plasmids, final_doc = assembly.run() # TODO upload product_doc? - self.last_assembly_pudu_json = legacy_assembly_routes_to_pudu_json( + lvl2_pudu_payloads = legacy_assembly_routes_to_pudu_json( product_plasmids=lvl2_plasmids, part_plasmid_routes=[lvl1_plasmids for _ in range(len(lvl2_plasmids))], backbones=[backbone for _ in range(len(lvl2_plasmids))], restriction_enzymes=[self.BbsI_impl for _ in range(len(lvl2_plasmids))], ) + self.last_assembly_pudu_json = lvl2_pudu_payloads + self.last_assembly_pudu_json_by_stage["assembly_lvl1"] = lvl1_pudu_payloads + self.last_assembly_pudu_json_by_stage["assembly_lvl2"] = list(lvl2_pudu_payloads) self.indexed_plasmids.extend(lvl2_plasmids) return lvl2_plasmids, final_doc @@ -1065,6 +1094,842 @@ def plating( "protocol_artifacts": protocol_artifacts, } + def full_build( + self, + designs: Any = None, + results_dir: str | Path = "full_build_results", + overwrite: bool = False, + chassis_name: str = "E_coli_DH5alpha", + plating_protocol_type: str = "manual", + plating_advanced_params: dict | None = None, + product_name: str = "full_build", + ) -> Dict[str, Any]: + """Run the legacy full build workflow and return packaged artifacts. + + The workflow is deliberately file-oriented: each stage writes explicit + intermediates/protocol inputs under ``results_dir`` and the return value + includes a zip archive containing those artifacts. + """ + + results_path = Path(results_dir) + if results_path.exists() and overwrite: + shutil.rmtree(results_path) + results_path.mkdir(parents=True, exist_ok=True) + self.last_assembly_pudu_json = [] + self.last_assembly_pudu_json_by_stage = {} + + result: Dict[str, Any] = { + "results_dir": str(results_path), + "domestication": {"successful": [], "failed": []}, + "assembly_lvl1": {"successful": [], "failed": []}, + "assembly_lvl2": {"successful": [], "failed": []}, + "transformation": {"successful": [], "failed": []}, + "plating": {"successful": [], "failed": []}, + "skipped": [], + "artifacts": [], + } + assembly_payloads: Dict[str, list[dict[str, object]]] = { + "assembly_lvl1": [], + "assembly_lvl2": [], + "domestication": [], + } + + lvl2_docs, lvl1_designs = self._split_full_build_inputs(designs) + + for index, lvl2_doc in enumerate(lvl2_docs, start=1): + label = f"lvl2_{index}" + try: + lvl2_products, lvl2_doc_out = self._run_full_build_lvl2( + lvl2_doc, + product_name=f"{product_name}_{label}", + result=result, + assembly_payloads=assembly_payloads, + chassis_name=chassis_name, + results_path=results_path, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + ) + self._run_transformation_and_plating( + lvl2_products, + stage_label=f"{label}_final", + result=result, + results_path=results_path, + chassis_name=chassis_name, + transformation_doc=lvl2_doc_out, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + ) + except Exception as exc: + result["assembly_lvl2"]["failed"].append( + {"design": label, "error": str(exc)} + ) + + if lvl1_designs: + self._run_full_build_lvl1_designs( + lvl1_designs, + result=result, + assembly_payloads=assembly_payloads, + chassis_name=chassis_name, + results_path=results_path, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + product_name=product_name, + ) + elif not lvl2_docs: + result["skipped"].append( + {"stage": "assembly_lvl2", "reason": "no level-2 design provided"} + ) + + artifact_paths = self._write_full_build_artifacts( + result=result, + assembly_payloads=assembly_payloads, + results_path=results_path, + ) + result["artifacts"].extend(str(path) for path in artifact_paths) + + manifest_path = results_path / "full_build_manifest.json" + zip_path = self._resolve_full_build_zip_path(results_path, overwrite=overwrite) + result["manifest_path"] = str(manifest_path) + result["zip_path"] = str(zip_path) + result["artifact_zip"] = str(zip_path) + self._write_json(manifest_path, result) + self._archive_full_build_results(results_path, zip_path) + + return result + + def _split_full_build_inputs( + self, designs: Any + ) -> tuple[list[sbol2.Document], list[sbol2.ComponentDefinition]]: + if designs is None: + return [], [self._get_abstract_design()] + if isinstance(designs, sbol2.Document): + return [designs], [] + if isinstance(designs, sbol2.CombinatorialDerivation): + return [], self._normalize_full_build_designs(designs) + if isinstance(designs, sbol2.ComponentDefinition): + return [], [designs] + if isinstance(designs, list) or isinstance(designs, tuple): + lvl2_docs = [item for item in designs if isinstance(item, sbol2.Document)] + lvl1_inputs = [ + item for item in designs if not isinstance(item, sbol2.Document) + ] + lvl1_designs = ( + self._normalize_full_build_designs(lvl1_inputs) + if lvl1_inputs + else [] + ) + return lvl2_docs, lvl1_designs + return [], self._normalize_full_build_designs(designs) + + def _run_full_build_lvl2( + self, + lvl2_doc: sbol2.Document, + *, + product_name: str, + result: Dict[str, Any], + assembly_payloads: Dict[str, list[dict[str, object]]], + chassis_name: str, + results_path: Path, + plating_protocol_type: str, + plating_advanced_params: dict | None, + overwrite: bool, + ) -> tuple[list[Any], sbol2.Document]: + try: + lvl2_products, lvl2_doc_out = self.assembly_lvl2( + lvl2_doc, product_name=product_name + ) + result["assembly_lvl2"]["successful"].append( + { + "design": self._document_label(lvl2_doc), + "products": self._product_identities(lvl2_products), + } + ) + self._capture_assembly_payloads(assembly_payloads) + return list(lvl2_products), lvl2_doc_out + except Exception as lvl2_exc: + result["assembly_lvl2"]["failed"].append( + { + "design": self._document_label(lvl2_doc), + "error": str(lvl2_exc), + "recovery": "attempting level-1 assembly and domestication", + } + ) + + tus = _extract_lvl2_TUs(lvl2_doc) + lvl1_products = self._attempt_lvl1_then_domesticate( + tus, + result=result, + assembly_payloads=assembly_payloads, + product_name=f"{product_name}_lvl1", + results_path=results_path, + chassis_name=chassis_name, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + ) + if lvl1_products: + self._run_transformation_and_plating( + lvl1_products, + stage_label=f"{product_name}_lvl1", + result=result, + results_path=results_path, + chassis_name=chassis_name, + transformation_doc=self.sbol_doc, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + ) + + lvl2_products, lvl2_doc_out = self.assembly_lvl2( + lvl2_doc, product_name=product_name + ) + result["assembly_lvl2"]["successful"].append( + { + "design": self._document_label(lvl2_doc), + "products": self._product_identities(lvl2_products), + "after_recovery": True, + } + ) + self._capture_assembly_payloads(assembly_payloads) + return list(lvl2_products), lvl2_doc_out + + def _run_full_build_lvl1_designs( + self, + designs: list[sbol2.ComponentDefinition], + *, + result: Dict[str, Any], + assembly_payloads: Dict[str, list[dict[str, object]]], + chassis_name: str, + results_path: Path, + plating_protocol_type: str, + plating_advanced_params: dict | None, + overwrite: bool, + product_name: str, + ) -> None: + missing_parts = [] + seen_missing = set() + for design in designs: + for missing in self._find_missing_parts_for_lvl1(design): + part = missing["part"] + if part.identity not in seen_missing: + missing_parts.append(part) + seen_missing.add(part.identity) + + if missing_parts: + self._run_domestication( + missing_parts, + result=result, + assembly_payloads=assembly_payloads, + results_path=results_path, + chassis_name=chassis_name, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + ) + + for design in designs: + try: + products, stage_doc = self._run_one_lvl1_design( + design, + result=result, + assembly_payloads=assembly_payloads, + product_name=product_name, + ) + except Exception as exc: + result["assembly_lvl1"]["failed"].append( + {"design": design.displayId or design.identity, "error": str(exc)} + ) + continue + self._run_transformation_and_plating( + products, + stage_label=design.displayId or "lvl1", + result=result, + results_path=results_path, + chassis_name=chassis_name, + transformation_doc=stage_doc, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + ) + + result["skipped"].append( + {"stage": "assembly_lvl2", "reason": "no level-2 design provided"} + ) + + def _attempt_lvl1_then_domesticate( + self, + designs: list[sbol2.ComponentDefinition], + *, + result: Dict[str, Any], + assembly_payloads: Dict[str, list[dict[str, object]]], + product_name: str, + results_path: Path, + chassis_name: str, + plating_protocol_type: str, + plating_advanced_params: dict | None, + overwrite: bool, + ) -> list[Any]: + products: list[Any] = [] + failed_designs: list[sbol2.ComponentDefinition] = [] + for design in designs: + try: + design_products, _ = self._run_one_lvl1_design( + design, + result=result, + assembly_payloads=assembly_payloads, + product_name=product_name, + ) + products.extend(design_products) + except Exception as exc: + result["assembly_lvl1"]["failed"].append( + {"design": design.displayId or design.identity, "error": str(exc)} + ) + failed_designs.append(design) + + missing_parts = [] + seen_missing = set() + for design in failed_designs: + for missing in self._find_missing_parts_for_lvl1(design): + part = missing["part"] + if part.identity not in seen_missing: + missing_parts.append(part) + seen_missing.add(part.identity) + + if missing_parts: + self._run_domestication( + missing_parts, + result=result, + assembly_payloads=assembly_payloads, + results_path=results_path, + chassis_name=chassis_name, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + ) + + for design in failed_designs: + design_products, _ = self._run_one_lvl1_design( + design, + result=result, + assembly_payloads=assembly_payloads, + product_name=product_name, + ) + products.extend(design_products) + + return products + + def _run_one_lvl1_design( + self, + design: sbol2.ComponentDefinition, + *, + result: Dict[str, Any], + assembly_payloads: Dict[str, list[dict[str, object]]], + product_name: str, + ) -> tuple[list[Any], sbol2.Document]: + output = self.assembly_lvl1([design], product_name=product_name) + products, stage_doc = self._normalize_lvl1_output(output, design) + result["assembly_lvl1"]["successful"].append( + { + "design": design.displayId or design.identity, + "products": self._product_identities(products), + } + ) + self._capture_assembly_payloads(assembly_payloads, default_stage="assembly_lvl1") + return products, stage_doc + + def _run_domestication( + self, + parts: list[sbol2.ComponentDefinition], + *, + result: Dict[str, Any], + assembly_payloads: Dict[str, list[dict[str, object]]], + results_path: Path, + chassis_name: str, + plating_protocol_type: str, + plating_advanced_params: dict | None, + overwrite: bool, + ) -> list[Any]: + try: + products = list(self.domestication(parts)) + except Exception as exc: + result["domestication"]["failed"].append( + { + "parts": [part.displayId or part.identity for part in parts], + "error": str(exc), + } + ) + return [] + + self._index_domestication_products(products) + + result["domestication"]["successful"].append( + { + "parts": [part.displayId or part.identity for part in parts], + "products": self._product_identities(products), + } + ) + self._capture_assembly_payloads(assembly_payloads, default_stage="domestication") + self._run_transformation_and_plating( + products, + stage_label="domestication", + result=result, + results_path=results_path, + chassis_name=chassis_name, + transformation_doc=self.sbol_doc, + plating_protocol_type=plating_protocol_type, + plating_advanced_params=plating_advanced_params, + overwrite=overwrite, + ) + return products + + + def _index_domestication_products(self, products: list[Any]) -> None: + """Make domesticated plasmids available to subsequent assembly retries.""" + for product in products: + if isinstance(product, Plasmid): + if not self._get_indexed_plasmid( + self.indexed_plasmids, product.plasmid_definition + ): + self.indexed_plasmids.append(product) + continue + + if isinstance(product, sbol2.ComponentDefinition): + self._sort_plasmid_components(product, self.sbol_doc) + + def _run_transformation_and_plating( + self, + products: list[Any], + *, + stage_label: str, + result: Dict[str, Any], + results_path: Path, + chassis_name: str, + transformation_doc: sbol2.Document, + plating_protocol_type: str, + plating_advanced_params: dict | None, + overwrite: bool, + ) -> None: + if not products: + return + try: + transformation_result = self.transformation( + products, + chassis_name=chassis_name, + transformation_doc=transformation_doc, + ) + result["transformation"]["successful"].append( + { + "stage_label": stage_label, + "products": self._product_identities(products), + "result": transformation_result, + } + ) + except Exception as exc: + result["transformation"]["failed"].append( + { + "stage_label": stage_label, + "products": self._product_identities(products), + "error": str(exc), + } + ) + return + + try: + plating_result = self.plating( + transformation_result, + results_dir=results_path / f"{stage_label}_plating", + protocol_type=plating_protocol_type, + advanced_params=plating_advanced_params, + plating_doc=transformation_doc, + overwrite=overwrite, + ) + result["plating"]["successful"].append( + {"stage_label": stage_label, "result": plating_result} + ) + except Exception as exc: + result["plating"]["failed"].append( + {"stage_label": stage_label, "error": str(exc)} + ) + + def _normalize_full_build_designs(self, designs: Any) -> list[sbol2.ComponentDefinition]: + if isinstance(designs, sbol2.ComponentDefinition): + return [designs] + if isinstance(designs, sbol2.CombinatorialDerivation): + return self._expand_combinatorial_derivation(designs) + if isinstance(designs, list) or isinstance(designs, tuple): + normalized: list[sbol2.ComponentDefinition] = [] + for design in designs: + if isinstance(design, sbol2.CombinatorialDerivation): + normalized.extend(self._expand_combinatorial_derivation(design)) + elif isinstance(design, sbol2.ComponentDefinition): + normalized.append(design) + else: + raise ValueError( + "full_build designs must be SBOL ComponentDefinitions, " + "CombinatorialDerivations, Documents, or lists of those." + ) + return normalized + raise ValueError( + "full_build designs must be SBOL ComponentDefinitions, " + "CombinatorialDerivations, Documents, or lists of those." + ) + + def _expand_combinatorial_derivation( + self, + derivation: sbol2.CombinatorialDerivation, + product_name_prefix: str = "full_build", + ) -> list[sbol2.ComponentDefinition]: + template = get_or_pull( + self.sbol_doc, self.sbh, derivation.masterTemplate, self.server_mode + ) + variant_lists = enumerate_design_variants( + extract_combinatorial_design_parts(template, derivation) + ) + variants = [] + for index, parts in enumerate(variant_lists, start=1): + variant = sbol2.ComponentDefinition( + f"{product_name_prefix}_variant_{index:03d}" + ) + self.sbol_doc.add(variant) + created_components = [] + for part_index, part in enumerate(parts, start=1): + component = variant.components.create(f"part_{part_index}") + component.definition = part.identity + created_components.append(component) + for constraint_index in range(len(created_components) - 1): + constraint = variant.sequenceConstraints.create( + f"constraint_{constraint_index + 1}" + ) + constraint.subject = created_components[constraint_index].identity + constraint.object = created_components[constraint_index + 1].identity + constraint.restriction = sbol2.SBOL_RESTRICTION_PRECEDES + variants.append(variant) + return variants + + def _find_missing_parts_for_lvl1( + self, design: sbol2.ComponentDefinition + ) -> list[dict[str, Any]]: + missing = [] + parts = self._extract_design_parts(design) + plasmid_dict = self._construct_plasmid_dict(parts, AMP) + for part in parts: + if not plasmid_dict.get(part.displayId): + missing.append({"part": part, "reason": "no implemented plasmid"}) + + if missing: + return missing + + backbone, compatible = self._get_backbone( + plasmid_dict, antibiotic_resistance=KAN + ) + if backbone is None or not compatible: + return [ + { + "part": part, + "reason": "no compatible level-1 route", + } + for part in parts + ] + return [] + + def _normalize_lvl1_output( + self, output: Any, design: sbol2.ComponentDefinition + ) -> tuple[list[Any], sbol2.Document]: + stage_doc = self.sbol_doc + payload = output + if isinstance(output, tuple): + payload = output[0] + if len(output) > 1 and isinstance(output[1], sbol2.Document): + stage_doc = output[1] + if isinstance(payload, dict): + products = list(payload.get(design.identity, [])) + if not products: + products = [ + product + for product_list in payload.values() + for product in ( + product_list + if isinstance(product_list, list) + else [product_list] + ) + ] + return products, stage_doc + if isinstance(payload, list): + return payload, stage_doc + return [payload], stage_doc + + def _capture_assembly_payloads( + self, + assembly_payloads: Dict[str, list[dict[str, object]]], + default_stage: str = "assembly_lvl1", + ) -> None: + staged = getattr(self, "last_assembly_pudu_json_by_stage", {}) or {} + if staged: + for stage, payloads in staged.items(): + assembly_payloads.setdefault(stage, []) + assembly_payloads[stage].extend(self._dedupe_payloads(payloads)) + return + payloads = getattr(self, "last_assembly_pudu_json", []) or [] + assembly_payloads.setdefault(default_stage, []) + assembly_payloads[default_stage].extend(self._dedupe_payloads(payloads)) + + def _write_full_build_artifacts( + self, + *, + result: Dict[str, Any], + assembly_payloads: Dict[str, list[dict[str, object]]], + results_path: Path, + ) -> list[Path]: + written: list[Path] = [] + + for stage, payloads in sorted(assembly_payloads.items()): + payloads = self._dedupe_payloads(payloads) + if not payloads: + continue + json_path = write_assembly_pudu_input_json( + payloads, results_path / f"{stage}_pudu_assembly_input.json" + ) + written.append(json_path) + written.append( + self._write_pudu_assembly_protocol_script( + results_path / f"{stage}_pudu_assembly_protocol.py", + payloads, + protocol_name=f"BuildCompiler {stage} Assembly", + ) + ) + + transformation_payloads = [] + plasmid_location_inputs = [] + for entry in result["transformation"]["successful"]: + products = entry.get("products", []) + tx_result = entry.get("result", {}) + artifacts = tx_result.get("sbol_artifacts", []) if isinstance(tx_result, dict) else [] + strain_ids = [ + artifact.get("transformed_strain_module") + or artifact.get("transformed_strain_implementation") + for artifact in artifacts + if isinstance(artifact, dict) + ] + if not strain_ids: + strain_ids = [f"{entry.get('stage_label', 'transformation')}_strain"] + chassis = ( + tx_result.get("chassis", "E_coli_DH5alpha") + if isinstance(tx_result, dict) + else "E_coli_DH5alpha" + ) + transformation_payloads.extend( + transformations_to_pudu_json( + strain_identities=strain_ids, + chassis_identities=[chassis for _ in strain_ids], + plasmid_sets=self._plasmid_sets_for_transformed_strains( + products, strain_ids + ), + ) + ) + plasmid_location_inputs.extend(products) + + if transformation_payloads: + tx_path = results_path / "transformation_pudu_input.json" + self._write_json(tx_path, transformation_payloads) + written.append(tx_path) + location_payload = plasmid_locations_to_pudu_json(plasmid_location_inputs) + loc_path = results_path / "transformation_plasmid_locations.json" + self._write_json(loc_path, location_payload) + written.append(loc_path) + written.append( + self._write_pudu_transformation_protocol_script( + results_path / "pudu_transformation_protocol.py", + transformation_payloads, + location_payload, + ) + ) + + plating_payloads = [] + for entry in result["plating"]["successful"]: + plating_result = entry.get("result", {}) + if not isinstance(plating_result, dict): + continue + plating_data = ( + plating_result.get("json_intermediate", {}).get("plating_data", {}) + ) + bacterium_locations = plating_data.get("bacterium_locations") + if bacterium_locations: + plating_payloads.append( + plating_to_pudu_json(bacterium_locations=bacterium_locations) + ) + + if plating_payloads: + plating_payload = ( + plating_payloads[0] + if len(plating_payloads) == 1 + else {"batches": plating_payloads} + ) + plating_path = results_path / "plating_pudu_input.json" + self._write_json(plating_path, plating_payload) + written.append(plating_path) + written.append( + self._write_pudu_plating_protocol_script( + results_path / "pudu_plating_protocol.py", plating_payload + ) + ) + + return written + + def _plasmid_sets_for_transformed_strains( + self, products: list[Any], strain_ids: list[str] + ) -> list[list[str]]: + product_ids = self._product_identities(products) + if len(product_ids) == len(strain_ids): + return [[product_id] for product_id in product_ids] + return [product_ids for _ in strain_ids] + + def _write_pudu_assembly_protocol_script( + self, path: Path, payload: list[dict[str, object]], protocol_name: str + ) -> Path: + script = ( + "from pudu.assembly import SBOLLoopAssembly\n" + "from opentrons import protocol_api\n\n" + f"assembly_data = {json.dumps(payload, indent=4)}\n\n" + "metadata = {\n" + f" 'protocolName': {protocol_name!r},\n" + " 'author': 'BuildCompiler',\n" + " 'apiLevel': '2.21',\n" + "}\n\n" + "def run(protocol: protocol_api.ProtocolContext):\n" + " protocol_instance = SBOLLoopAssembly(assembly_data=assembly_data)\n" + " protocol_instance.run(protocol)\n" + ) + path.write_text(script, encoding="utf-8") + return path + + def _write_pudu_transformation_protocol_script( + self, + path: Path, + transformation_payload: list[dict[str, object]], + plasmid_locations: dict[str, list[str]], + ) -> Path: + script = ( + "from pudu.transformation import HeatShockTransformation\n" + "from opentrons import protocol_api\n\n" + f"transformation_data = {json.dumps(transformation_payload, indent=4)}\n\n" + f"plasmid_locations = {json.dumps(plasmid_locations, indent=4)}\n\n" + "metadata = {\n" + " 'protocolName': 'BuildCompiler Transformation',\n" + " 'author': 'BuildCompiler',\n" + " 'apiLevel': '2.21',\n" + "}\n\n" + "def run(protocol: protocol_api.ProtocolContext):\n" + " protocol_instance = HeatShockTransformation(\n" + " transformation_data=transformation_data,\n" + " plasmid_locations=plasmid_locations,\n" + " )\n" + " protocol_instance.run(protocol)\n" + ) + path.write_text(script, encoding="utf-8") + return path + + def _write_pudu_plating_protocol_script( + self, path: Path, plating_payload: dict[str, object] + ) -> Path: + script = ( + "from pudu.plating import Plating\n" + "from opentrons import protocol_api\n\n" + f"plating_data = {json.dumps(plating_payload, indent=4)}\n\n" + "metadata = {\n" + " 'protocolName': 'BuildCompiler Plating',\n" + " 'author': 'BuildCompiler',\n" + " 'apiLevel': '2.21',\n" + "}\n\n" + "def run(protocol: protocol_api.ProtocolContext):\n" + " protocol_instance = Plating(plating_data=plating_data)\n" + " protocol_instance.run(protocol)\n" + ) + path.write_text(script, encoding="utf-8") + return path + + def _resolve_full_build_zip_path( + self, results_path: Path, overwrite: bool + ) -> Path: + zip_path = results_path.with_suffix(".zip") + if zip_path.exists() and overwrite: + zip_path.unlink() + if zip_path.exists(): + index = 1 + while True: + candidate = results_path.with_name(f"{results_path.name}_{index}.zip") + if not candidate.exists(): + zip_path = candidate + break + index += 1 + return zip_path + + def _archive_full_build_results( + self, results_path: Path, zip_path: Path + ) -> Path: + with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as archive: + for path in sorted(results_path.rglob("*")): + if path.is_file(): + archive.write(path, path.relative_to(results_path)) + return zip_path + + def _write_json(self, path: Path, payload: Any) -> Path: + path.write_text( + json.dumps(self._json_safe(payload), indent=2) + "\n", + encoding="utf-8", + ) + return path + + def _json_safe(self, value: Any) -> Any: + if isinstance(value, Path): + return str(value) + if isinstance(value, dict): + return {str(key): self._json_safe(item) for key, item in value.items()} + if isinstance(value, (list, tuple, set)): + return [self._json_safe(item) for item in value] + if isinstance(value, (str, int, float, bool)) or value is None: + return value + if hasattr(value, "identity"): + return getattr(value, "identity") + if hasattr(value, "plasmid_definition"): + return self._plasmid_identity(value) + return str(value) + + def _product_identities(self, products: list[Any]) -> list[str]: + return [self._plasmid_identity(product) for product in products] + + def _plasmid_identity(self, product: Any) -> str: + if isinstance(product, str): + return product + definition = getattr(product, "plasmid_definition", product) + return str( + getattr(definition, "identity", None) + or getattr(definition, "displayId", None) + or product + ) + + def _document_label(self, doc: sbol2.Document) -> str: + try: + top_level = extract_toplevel_definition(doc) + return top_level.displayId or top_level.identity + except Exception: + return "level_2_design" + + def _dedupe_payloads( + self, payloads: list[dict[str, object]] + ) -> list[dict[str, object]]: + deduped = [] + seen = set() + for payload in payloads or []: + key = json.dumps(self._json_safe(payload), sort_keys=True) + if key in seen: + continue + seen.add(key) + deduped.append(payload) + return deduped + def _extract_plasmids_from_strain( self, strain: sbol2.ModuleDefinition, @@ -1643,6 +2508,57 @@ def _add_if_absent(self, doc: sbol2.Document, obj: Any): if doc.find(obj.identity) is None: doc.add(obj) + def _normalize_transformation_inputs( + self, assembly_products: List[Any] + ) -> List[Dict[str, sbol2.ComponentDefinition | str]]: + """Normalize supported transformation inputs into plasmid definitions. + + Transformation can be called directly after assembly, where inputs are + BuildCompiler ``Plasmid`` objects, or independently with SBOL plasmid + definitions/dict payloads. This keeps that adapter logic out of the SBOL + writer so transformation remains usable as a standalone stage. + """ + + normalized_products = [] + for product in assembly_products: + source = None + plasmid = None + + if isinstance(product, dict): + source = product.get("source") or product.get("name") + product = product.get("plasmid") or product.get("plasmid_definition") + + if isinstance(product, Plasmid): + plasmid = product.plasmid_definition + source = source or product.name or plasmid.displayId + elif isinstance(product, sbol2.ComponentDefinition): + plasmid = product + source = source or plasmid.displayId + elif hasattr(product, "plasmid_definition"): + plasmid = product.plasmid_definition + source = source or getattr(product, "name", None) or plasmid.displayId + + if plasmid is None: + raise ValueError( + "transformation inputs must be Plasmid objects, " + "sbol2.ComponentDefinition plasmids, or dictionaries with a " + "'plasmid' entry." + ) + if not isinstance(plasmid, sbol2.ComponentDefinition): + raise ValueError( + f"transformation plasmid input must resolve to a " + f"ComponentDefinition, got {type(plasmid).__name__}." + ) + + normalized_products.append( + { + "source": source or plasmid.displayId or plasmid.identity, + "plasmid": plasmid, + } + ) + + return normalized_products + def _get_or_create_chassis( self, doc: sbol2.Document, chassis_name: str ) -> tuple[sbol2.ModuleDefinition, sbol2.Implementation]: diff --git a/tests/test_buildcompiler_transformation.py b/tests/test_buildcompiler_transformation.py index 0b9a6e7..137ab88 100644 --- a/tests/test_buildcompiler_transformation.py +++ b/tests/test_buildcompiler_transformation.py @@ -1,11 +1,13 @@ import os import sys import unittest +from pathlib import Path import sbol2 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../src"))) +from buildcompiler.abstract_translator import extract_toplevel_definition from buildcompiler.buildcompiler import BuildCompiler from buildcompiler.constants import ENGINEERED_PLASMID @@ -59,6 +61,46 @@ def test_transformation_requires_inputs(self): with self.assertRaises(ValueError): self.compiler.transformation([]) + def test_transformation_accepts_lvl1_assembly_products(self): + test_files = Path(__file__).parent / "test_files" + collection_docs = [] + for filename in ( + "CIDARMoCloParts_collection.xml", + "CIDARMoCloPlasmidsKit_collection.xml", + "Enzyme_Implementations_collection.xml", + "impl_test_collection.xml", + ): + doc = sbol2.Document() + doc.read(str(test_files / filename)) + collection_docs.append(doc) + + design_doc = sbol2.Document() + design_doc.read(str(test_files / "abstract_design.xml")) + design = extract_toplevel_definition(design_doc) + compiler = BuildCompiler.from_local_documents( + collection_docs, design_doc=design_doc + ) + + assembly_routes, assembly_doc = compiler.assembly_lvl1( + [design], + final_doc=sbol2.Document(), + product_name="transformation_lvl1", + ) + + products = assembly_routes[design.identity] + result = compiler.transformation( + products, + chassis_name="E_coli_DH5alpha", + transformation_doc=assembly_doc, + ) + + self.assertEqual(result["stage"], "transformation") + self.assertEqual(result["inputs"], [products[0].name]) + self.assertEqual(len(result["sbol_artifacts"]), 1) + self.assertIsNotNone( + assembly_doc.find(result["sbol_artifacts"][0]["transformation_activity"]) + ) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_full_build.py b/tests/test_full_build.py index eb3a532..071d9ae 100644 --- a/tests/test_full_build.py +++ b/tests/test_full_build.py @@ -1,3 +1,4 @@ +import json import os import sys import tempfile @@ -11,6 +12,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../src"))) from buildcompiler.buildcompiler import BuildCompiler +from buildcompiler.plasmid import Plasmid from buildcompiler.constants import ENGINEERED_PLASMID @@ -57,6 +59,16 @@ def _make_plasmid(self, display_id: str) -> sbol2.ComponentDefinition: self.doc.add(plasmid) return plasmid + def _make_lvl2_document(self) -> tuple[sbol2.Document, sbol2.ComponentDefinition]: + doc = sbol2.Document() + tu = sbol2.ComponentDefinition("example_tu") + lvl2 = sbol2.ComponentDefinition("example_lvl2_design") + doc.add(tu) + doc.add(lvl2) + comp = lvl2.components.create("tu_component") + comp.definition = tu.identity + return doc, tu + def test_normalize_full_build_designs_input_shapes(self): d1 = self._make_design("design_a", ["part_a"]) d2 = self._make_design("design_b", ["part_b"]) @@ -108,6 +120,44 @@ def test_find_missing_parts_reports_missing_and_present(self): self.assertEqual(missing[0]["part"].displayId, "part_y") self.assertEqual(missing[0]["reason"], "no implemented plasmid") + def test_run_domestication_indexes_products_before_retry(self): + missing_part = self._make_part("missing_for_retry") + domesticated = self._make_plasmid("domesticated_for_retry") + + with tempfile.TemporaryDirectory() as tmpdir, patch.object( + self.compiler, "domestication", return_value=[domesticated] + ), patch.object( + self.compiler, "_sort_plasmid_components" + ) as mock_sort, patch.object( + self.compiler, "_run_transformation_and_plating" + ): + self.compiler._run_domestication( + [missing_part], + result={ + "domestication": {"successful": [], "failed": []}, + "transformation": {"successful": [], "failed": []}, + "plating": {"successful": [], "failed": []}, + }, + assembly_payloads={}, + results_path=Path(tmpdir), + chassis_name="E_coli_DH5alpha", + plating_protocol_type="manual", + plating_advanced_params=None, + overwrite=True, + ) + + mock_sort.assert_called_once_with(domesticated, self.compiler.sbol_doc) + + def test_index_domestication_products_adds_plasmid_routes_once(self): + domesticated = self._make_plasmid("domesticated_route_for_retry") + route = object.__new__(Plasmid) + route.plasmid_definition = domesticated + + self.compiler._index_domestication_products([route]) + self.compiler._index_domestication_products([route]) + + self.assertEqual(self.compiler.indexed_plasmids, [route]) + def test_full_build_orchestration_and_stage_skip(self): design_a = self._make_design("dA", ["pa"]) design_b = self._make_design("dB", ["pb"]) @@ -173,6 +223,181 @@ def test_full_build_writes_manifest_and_zip_and_return_shape(self): names = archive.namelist() self.assertIn("full_build_manifest.json", names) + def test_full_build_pudu_transformation_pairs_each_strain_with_own_product(self): + result = { + "transformation": { + "successful": [ + { + "stage_label": "assembly_lvl1", + "products": ["plasmid_a", "plasmid_b"], + "result": { + "chassis": "E_coli_DH5alpha", + "sbol_artifacts": [ + {"transformed_strain_module": "strain_a"}, + {"transformed_strain_module": "strain_b"}, + ], + }, + } + ] + }, + "plating": {"successful": []}, + } + + with tempfile.TemporaryDirectory() as tmpdir: + self.compiler._write_full_build_artifacts( + result=result, + assembly_payloads={}, + results_path=Path(tmpdir), + ) + payload = json.loads( + (Path(tmpdir) / "transformation_pudu_input.json").read_text( + encoding="utf-8" + ) + ) + + self.assertEqual( + payload, + [ + { + "Strain": "strain_a", + "Chassis": "E_coli_DH5alpha", + "Plasmids": ["plasmid_a"], + }, + { + "Strain": "strain_b", + "Chassis": "E_coli_DH5alpha", + "Plasmids": ["plasmid_b"], + }, + ], + ) + + def test_full_build_lvl2_example_packages_pudu_protocols_for_recovery_stack(self): + lvl2_doc, _ = self._make_lvl2_document() + missing_part = self._make_part("missing_promoter") + domesticated = self._make_plasmid("domesticated_missing_promoter") + lvl1_product = self._make_plasmid("assembled_example_tu") + lvl2_product = self._make_plasmid("assembled_example_lvl2") + calls = [] + + def fake_assembly_lvl2(*args, **kwargs): + calls.append("assembly_lvl2") + if calls.count("assembly_lvl2") == 1: + raise RuntimeError("level-2 input is missing level-1 regions") + self.compiler.last_assembly_pudu_json_by_stage = { + "assembly_lvl2": [ + { + "Product": lvl2_product.identity, + "Backbone": "lvl2_backbone", + "PartsList": [lvl1_product.identity], + "Restriction Enzyme": "BbsI", + } + ] + } + return [lvl2_product], self.doc + + def fake_assembly_lvl1(*args, **kwargs): + calls.append("assembly_lvl1") + if calls.count("assembly_lvl1") == 1: + raise RuntimeError("level-1 input is missing domesticated part") + self.compiler.last_assembly_pudu_json_by_stage = { + "assembly_lvl1": [ + { + "Product": lvl1_product.identity, + "Backbone": "lvl1_backbone", + "PartsList": [domesticated.identity], + "Restriction Enzyme": "BsaI", + } + ] + } + return [lvl1_product], self.doc + + def fake_domestication(parts): + calls.append("domestication") + self.compiler.last_assembly_pudu_json_by_stage = { + "domestication": [ + { + "Product": domesticated.identity, + "Backbone": "domestication_backbone", + "PartsList": [missing_part.identity], + "Restriction Enzyme": "BsaI", + } + ] + } + return [domesticated] + + def fake_transformation( + products, chassis_name="E_coli_DH5alpha", transformation_doc=None + ): + calls.append("transformation") + product_id = products[0].identity + return { + "stage": "transformation", + "chassis": chassis_name, + "sbol_artifacts": [ + { + "transformed_strain_module": f"{product_id}_strain", + "transformed_strain_implementation": f"{product_id}_strain_impl", + } + ], + } + + def fake_plating(*args, **kwargs): + calls.append("plating") + return { + "stage": "plating", + "json_intermediate": { + "plating_data": { + "bacterium_locations": {"A1": "example_transformed_strain"} + } + }, + } + + with tempfile.TemporaryDirectory() as tmpdir, patch.object( + self.compiler, "assembly_lvl2", side_effect=fake_assembly_lvl2 + ), patch.object( + self.compiler, "assembly_lvl1", side_effect=fake_assembly_lvl1 + ), patch.object( + self.compiler, + "_find_missing_parts_for_lvl1", + return_value=[{"part": missing_part}], + ), patch.object( + self.compiler, "domestication", side_effect=fake_domestication + ), patch.object( + self.compiler, "transformation", side_effect=fake_transformation + ), patch.object( + self.compiler, "plating", side_effect=fake_plating + ): + result = self.compiler.full_build( + designs=lvl2_doc, + results_dir=Path(tmpdir) / "lvl2_full_build", + overwrite=True, + ) + zip_path = Path(result["zip_path"]) + self.assertTrue(zip_path.exists()) + with zipfile.ZipFile(zip_path, "r") as archive: + names = set(archive.namelist()) + + self.assertEqual( + calls[:3], ["assembly_lvl2", "assembly_lvl1", "domestication"] + ) + self.assertEqual(result["artifact_zip"], result["zip_path"]) + + expected_artifacts = { + "assembly_lvl1_pudu_assembly_input.json", + "assembly_lvl2_pudu_assembly_input.json", + "domestication_pudu_assembly_input.json", + "assembly_lvl1_pudu_assembly_protocol.py", + "assembly_lvl2_pudu_assembly_protocol.py", + "domestication_pudu_assembly_protocol.py", + "transformation_pudu_input.json", + "transformation_plasmid_locations.json", + "pudu_transformation_protocol.py", + "plating_pudu_input.json", + "pudu_plating_protocol.py", + "full_build_manifest.json", + } + self.assertTrue(expected_artifacts.issubset(names)) + if __name__ == "__main__": unittest.main() diff --git a/tests/unit/adapters/pudu/test_plating_json.py b/tests/unit/adapters/pudu/test_plating_json.py index aa075e5..320b660 100644 --- a/tests/unit/adapters/pudu/test_plating_json.py +++ b/tests/unit/adapters/pudu/test_plating_json.py @@ -3,17 +3,17 @@ def test_plating_to_pudu_json_shape_and_values(): payload = plating_to_pudu_json( - bacterium_locations={"strain_b": "B2", "strain_a": "A1"}, + bacterium_locations={"B2": "strain_b", "A1": "strain_a"}, advanced_parameters={"replicates": 2}, ) assert payload == { - "bacterium_locations": {"strain_a": "A1", "strain_b": "B2"}, - "advanced_parameters": {"replicates": 2}, + "bacterium_locations": {"A1": "strain_a", "B2": "strain_b"}, + "replicates": 2, } -def test_plating_to_pudu_json_defaults_advanced_parameters(): - payload = plating_to_pudu_json(bacterium_locations={"strain_a": "A1"}) +def test_plating_to_pudu_json_omits_empty_advanced_parameters(): + payload = plating_to_pudu_json(bacterium_locations={"A1": "strain_a"}) - assert payload["advanced_parameters"] == {} + assert payload == {"bacterium_locations": {"A1": "strain_a"}} diff --git a/tests/unit/adapters/pudu/test_transformation_json.py b/tests/unit/adapters/pudu/test_transformation_json.py index d18f7ab..8ad86ce 100644 --- a/tests/unit/adapters/pudu/test_transformation_json.py +++ b/tests/unit/adapters/pudu/test_transformation_json.py @@ -1,4 +1,5 @@ from buildcompiler.adapters.pudu import ( + plasmid_locations_to_pudu_json, transformation_to_pudu_json, transformations_to_pudu_json, ) @@ -36,3 +37,24 @@ def test_transformations_to_pudu_json_batch_helper_is_deterministic(): {"Strain": "s1", "Chassis": "c1", "Plasmids": ["p1"]}, {"Strain": "s2", "Chassis": "c2", "Plasmids": ["p2", "p3"]}, ] + + +def test_plasmid_locations_to_pudu_json_uses_deterministic_wells(): + payload = plasmid_locations_to_pudu_json(["p1", "p2", "p3"]) + + assert payload == { + "p1": ["A1"], + "p2": ["B1"], + "p3": ["C1"], + } + + +def test_plasmid_locations_to_pudu_json_accepts_explicit_wells_and_duplicates(): + payload = plasmid_locations_to_pudu_json( + ["p1", "p1", "p2"], wells=["A1", "B1", "C1"] + ) + + assert payload == { + "p1": ["A1", "B1"], + "p2": ["C1"], + }