diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..ae9cfdf --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,52 @@ +name: Documentation + +on: + push: + branches: + - main + +permissions: + contents: write + +concurrency: + group: docs-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-readthedocs: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install documentation dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e . + python -m pip install -r docs/requirements.txt + + - name: Update API reference record + run: python tools/update_api_reference.py + + - name: Build Read the Docs site + run: sphinx-build -b html -W --keep-going docs docs/_build/html + + - name: Commit API reference updates + run: | + if git diff --quiet -- docs/API_Reference.rst; then + echo "API reference is already current." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add docs/API_Reference.rst + git commit -m "docs: update API reference" + git push diff --git a/README.md b/README.md index d409123..308e3e1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # BuildCompiler +[![Documentation Status](https://readthedocs.org/projects/buildcompiler/badge/?version=latest)](https://buildcompiler.readthedocs.io/en/latest/?badge=latest) + BuildCompiler is a Python compiler pipeline for synthetic biology build planning. It takes abstract SBOL designs and indexed biological inventory, then produces an executable build plan across domestication, MoClo assembly level 1, MoClo assembly level 2, transformation, and plating. This repository is being refactored around a clean architecture. The existing codebase is useful as working evidence, especially the level-1 assembly path and SBOL digestion/ligation behavior, but the new implementation should not preserve old APIs, old import paths, or legacy module boundaries except the root package name `buildcompiler`. @@ -12,6 +14,8 @@ Designing a genetic construct is easier than building it in the lab. BuildCompil abstract SBOL design + inventory -> build plan -> SBOL build artifacts -> PUDU JSON -> optional manual/OT-2 protocols ``` +The Read the Docs site is available at [buildcompiler.readthedocs.io](https://buildcompiler.readthedocs.io/en/latest/) and is built from the Sphinx documentation in `docs/`. + The compiler should answer: - Can this design be built from current inventory? diff --git a/docs/API_Reference.rst b/docs/API_Reference.rst index 6052ef8..eb21fda 100644 --- a/docs/API_Reference.rst +++ b/docs/API_Reference.rst @@ -1,43 +1,411 @@ API Reference ============= +.. This file is generated by tools/update_api_reference.py. +.. Run `python tools/update_api_reference.py` after adding or removing public modules. + Public API ---------- -.. automodule:: buildcompiler.api +buildcompiler.api.compiler +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.api.compiler :members: :undoc-members: + :show-inheritance: -Legacy artifact-producing compiler ----------------------------------- +buildcompiler.api.options +~~~~~~~~~~~~~~~~~~~~~~~~~ -.. automodule:: buildcompiler.buildcompiler +.. automodule:: buildcompiler.api.options + :members: + :undoc-members: + :show-inheritance: + +Stages +------ + +buildcompiler.stages.assembly_lvl1 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.stages.assembly_lvl1 + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.stages.assembly_lvl2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.stages.assembly_lvl2 + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.stages.domestication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.stages.domestication + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.stages.transformation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.stages.transformation + :members: + :undoc-members: + :show-inheritance: + +Planning +-------- + +buildcompiler.planning.classifier +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.planning.classifier + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.planning.combinatorial +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.planning.combinatorial + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.planning.domestication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.planning.domestication + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.planning.full_build_planner +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.planning.full_build_planner + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.planning.models +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.planning.models + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.planning.validation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.planning.validation :members: :undoc-members: :show-inheritance: -PUDU adapters +Inventory +--------- + +buildcompiler.inventory.compatibility +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.inventory.compatibility + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.inventory.inventory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.inventory.inventory + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.inventory.selector +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.inventory.selector + :members: + :undoc-members: + :show-inheritance: + +SBOL helpers +------------ + +buildcompiler.sbol.assembly +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.sbol.assembly + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.sbol.domestication +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.sbol.domestication + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.sbol.resolver +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.sbol.resolver + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.sbol.transformation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.sbol.transformation + :members: + :undoc-members: + :show-inheritance: + +Domain models ------------- +buildcompiler.domain.approvals +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.approvals + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.build_request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.build_request + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.build_result +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.build_result + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.build_stage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.build_stage + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.design +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.design + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.material_state +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.material_state + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.missing_input +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.missing_input + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.plasmid +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.plasmid + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.reagent +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.reagent + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.status +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.status + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.domain.warnings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.domain.warnings + :members: + :undoc-members: + :show-inheritance: + +Execution +--------- + +buildcompiler.execution.context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.execution.context + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.execution.executor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.execution.executor + :members: + :undoc-members: + :show-inheritance: + +Adapters +-------- + +buildcompiler.adapters.opentrons.simulation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.adapters.opentrons.simulation + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.adapters.protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.adapters.protocols + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.adapters.pudu.assembly_json +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + .. automodule:: buildcompiler.adapters.pudu.assembly_json :members: :undoc-members: + :show-inheritance: + +buildcompiler.adapters.pudu.plating_json +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.adapters.pudu.plating_json + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.adapters.pudu.transformation_json +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. automodule:: buildcompiler.adapters.pudu.transformation_json :members: :undoc-members: + :show-inheritance: -.. automodule:: buildcompiler.adapters.pudu.plating_json +Reporting +--------- + +buildcompiler.reporting.graph +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.reporting.graph :members: :undoc-members: + :show-inheritance: -Planning and execution ----------------------- +buildcompiler.reporting.report +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. automodule:: buildcompiler.planning.full_build_planner +.. automodule:: buildcompiler.reporting.report :members: :undoc-members: + :show-inheritance: -.. automodule:: buildcompiler.execution.executor +buildcompiler.reporting.summary +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.reporting.summary + :members: + :undoc-members: + :show-inheritance: + +Legacy modules +-------------- + +buildcompiler.abstract_translator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.abstract_translator :members: :undoc-members: + :show-inheritance: + +buildcompiler.buildcompiler +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.buildcompiler + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.constants +~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.constants + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.plasmid +~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.plasmid + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.robotutils +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.robotutils + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.sbol2build +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.sbol2build + :members: + :undoc-members: + :show-inheritance: + +buildcompiler.transformation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: buildcompiler.transformation + :members: + :undoc-members: + :show-inheritance: diff --git a/tools/update_api_reference.py b/tools/update_api_reference.py new file mode 100644 index 0000000..5cd8053 --- /dev/null +++ b/tools/update_api_reference.py @@ -0,0 +1,103 @@ +"""Generate the Sphinx API reference page for BuildCompiler.""" + +from __future__ import annotations + +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +PACKAGE_ROOT = REPO_ROOT / "src" / "buildcompiler" +OUTPUT_PATH = REPO_ROOT / "docs" / "API_Reference.rst" + +SECTION_ORDER = [ + ("Public API", "buildcompiler.api"), + ("Stages", "buildcompiler.stages"), + ("Planning", "buildcompiler.planning"), + ("Inventory", "buildcompiler.inventory"), + ("SBOL helpers", "buildcompiler.sbol"), + ("Domain models", "buildcompiler.domain"), + ("Execution", "buildcompiler.execution"), + ("Adapters", "buildcompiler.adapters"), + ("Reporting", "buildcompiler.reporting"), + ("Legacy modules", "buildcompiler"), +] + +LEGACY_MODULES = { + "buildcompiler.abstract_translator", + "buildcompiler.buildcompiler", + "buildcompiler.constants", + "buildcompiler.plasmid", + "buildcompiler.robotutils", + "buildcompiler.sbol2build", + "buildcompiler.transformation", +} + +EXCLUDED_MODULES = {"buildcompiler.__init__"} + + +def underline(text: str, char: str = "=") -> str: + return char * len(text) + + +def module_name(path: Path) -> str: + relative = path.relative_to(REPO_ROOT / "src").with_suffix("") + return ".".join(relative.parts) + + +def discover_modules() -> list[str]: + modules = [] + for path in sorted(PACKAGE_ROOT.rglob("*.py")): + name = module_name(path) + if name.endswith(".__init__") or name in EXCLUDED_MODULES: + continue + modules.append(name) + return modules + + +def modules_for_section(modules: list[str], package: str) -> list[str]: + if package == "buildcompiler": + return [name for name in modules if name in LEGACY_MODULES] + prefix = f"{package}." + return [name for name in modules if name.startswith(prefix)] + + +def render_module(name: str) -> list[str]: + return [ + name, + underline(name, "~"), + "", + f".. automodule:: {name}", + " :members:", + " :undoc-members:", + " :show-inheritance:", + "", + ] + + +def render_api_reference() -> str: + modules = discover_modules() + lines = [ + "API Reference", + "=============", + "", + ".. This file is generated by tools/update_api_reference.py.", + ".. Run `python tools/update_api_reference.py` after adding or removing public modules.", + "", + ] + + for title, package in SECTION_ORDER: + section_modules = modules_for_section(modules, package) + if not section_modules: + continue + lines.extend([title, underline(title, "-"), ""]) + for name in section_modules: + lines.extend(render_module(name)) + + return "\n".join(lines).rstrip() + "\n" + + +def main() -> None: + OUTPUT_PATH.write_text(render_api_reference(), encoding="utf-8") + + +if __name__ == "__main__": + main()