From cb05c66b9e08503a59604d02204194ecf37bea2e Mon Sep 17 00:00:00 2001 From: John Major Date: Tue, 26 May 2026 16:19:01 -0700 Subject: [PATCH 01/47] Add Bloom container runtime --- .dockerignore | 16 + Dockerfile | 42 + bloom_lims/container_entry.py | 44 + container_tests/test_container_runtime.py | 56 + docker/entrypoint.sh | 16 + uv.lock | 2815 +++++++++++++++++++++ 6 files changed, 2989 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 bloom_lims/container_entry.py create mode 100644 container_tests/test_container_runtime.py create mode 100755 docker/entrypoint.sh create mode 100644 uv.lock diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..bfaf8e3 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +.git +.mypy_cache +.pytest_cache +.ruff_cache +.venv +__pycache__ +*.py[cod] +build +dist +*.egg-info +.coverage +htmlcov +tests +docs/plans +analytics +stage_to_delete diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..046d925 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,42 @@ +# syntax=docker/dockerfile:1.7 +ARG PYTHON_VERSION=3.12 + +FROM ghcr.io/astral-sh/uv:0.5.30-python${PYTHON_VERSION}-bookworm-slim AS builder + +ARG SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0 +ENV SETUPTOOLS_SCM_PRETEND_VERSION=${SETUPTOOLS_SCM_PRETEND_VERSION} \ + UV_COMPILE_BYTECODE=1 \ + UV_LINK_MODE=copy \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app +RUN apt-get update \ + && apt-get install -y --no-install-recommends build-essential libpq-dev \ + && rm -rf /var/lib/apt/lists/* +COPY pyproject.toml uv.lock README.md ./ +RUN uv sync --frozen --no-dev --no-install-project + +COPY bloom_lims ./bloom_lims +COPY main.py ./main.py +RUN uv sync --frozen --no-dev + +FROM python:${PYTHON_VERSION}-slim-bookworm AS runtime + +ENV PATH="/app/.venv/bin:${PATH}" \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +RUN addgroup --system lsmc && adduser --system --ingroup lsmc --home /app lsmc +WORKDIR /app +RUN apt-get update \ + && apt-get install -y --no-install-recommends libpq5 \ + && rm -rf /var/lib/apt/lists/* +COPY --from=builder --chown=lsmc:lsmc /app /app +COPY --chown=lsmc:lsmc docker/entrypoint.sh /entrypoint.sh +RUN chmod 0755 /entrypoint.sh +USER lsmc + +EXPOSE 8912 +ENTRYPOINT ["/entrypoint.sh"] +CMD ["python", "-m", "bloom_lims.container_entry"] diff --git a/bloom_lims/container_entry.py b/bloom_lims/container_entry.py new file mode 100644 index 0000000..4011afc --- /dev/null +++ b/bloom_lims/container_entry.py @@ -0,0 +1,44 @@ +"""Container foreground entrypoint for Bloom.""" + +from __future__ import annotations + +import os +from pathlib import Path + +def _required_env(name: str) -> str: + value = os.environ.get(name, "").strip() + if not value: + raise RuntimeError(f"{name} is required") + return value + + +def _required_absolute_file(name: str) -> Path: + path = Path(_required_env(name)) + if not path.is_absolute(): + raise RuntimeError(f"{name} must be an absolute path") + if not path.is_file(): + raise RuntimeError(f"{name} does not exist: {path}") + return path + + +def _start_server(**kwargs: object) -> None: + from bloom_lims.cli.server import start + + start(**kwargs) + + +def main() -> None: + _required_absolute_file("BLOOM_CONFIG_PATH") + _start_server( + port=int(_required_env("PORT")), + host=_required_env("HOST"), + reload=False, + background=False, + ssl=False, + cert=None, + key=None, + ) + + +if __name__ == "__main__": + main() diff --git a/container_tests/test_container_runtime.py b/container_tests/test_container_runtime.py new file mode 100644 index 0000000..4f93a29 --- /dev/null +++ b/container_tests/test_container_runtime.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +from pathlib import Path +from unittest.mock import patch + +import pytest + +from bloom_lims import container_entry + + +PROJECT_ROOT = Path(__file__).resolve().parents[1] + + +def test_docker_runtime_files_use_foreground_uv_and_no_legacy_runtime() -> None: + dockerfile = (PROJECT_ROOT / "Dockerfile").read_text(encoding="utf-8") + entrypoint = (PROJECT_ROOT / "docker" / "entrypoint.sh").read_text(encoding="utf-8") + + assert "uv sync --frozen --no-dev --no-install-project" in dockerfile + assert "uv sync --frozen --no-dev" in dockerfile + assert "USER lsmc" in dockerfile + assert "python\", \"-m\", \"bloom_lims.container_entry" in dockerfile + assert ":latest" not in dockerfile + assert "conda" not in dockerfile.lower() + assert "tmux" not in entrypoint + assert "background" not in entrypoint + assert "${BLOOM_CONFIG_PATH:?BLOOM_CONFIG_PATH is required}" in entrypoint + + +def test_container_entry_requires_absolute_config_path(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv("BLOOM_CONFIG_PATH", "relative.yaml") + + with pytest.raises(RuntimeError, match="must be an absolute path"): + container_entry._required_absolute_file("BLOOM_CONFIG_PATH") + + +def test_container_entry_runs_foreground_http_server( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + config_path = tmp_path / "bloom.yaml" + config_path.write_text("auth: {}\n", encoding="utf-8") + monkeypatch.setenv("BLOOM_CONFIG_PATH", str(config_path)) + monkeypatch.setenv("HOST", "127.0.0.1") + monkeypatch.setenv("PORT", "8912") + + with patch("bloom_lims.container_entry._start_server") as start: + container_entry.main() + + assert start.call_args.kwargs == { + "port": 8912, + "host": "127.0.0.1", + "reload": False, + "background": False, + "ssl": False, + "cert": None, + "key": None, + } diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000..e44ed84 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env sh +set -eu + +: "${LSMC_SERVICE_NAME:?LSMC_SERVICE_NAME is required}" +: "${LSMC_ENV:?LSMC_ENV is required}" +: "${LSMC_RUNTIME_CLASS:?LSMC_RUNTIME_CLASS is required}" +: "${BLOOM_CONFIG_PATH:?BLOOM_CONFIG_PATH is required}" +: "${HOST:?HOST is required}" +: "${PORT:?PORT is required}" +: "${OTEL_EXPORTER_OTLP_ENDPOINT:?OTEL_EXPORTER_OTLP_ENDPOINT is required}" + +export OTEL_SERVICE_NAME="${OTEL_SERVICE_NAME:-$LSMC_SERVICE_NAME}" +export OTEL_RESOURCE_ATTRIBUTES="service.name=${OTEL_SERVICE_NAME},deployment.environment=${LSMC_ENV},service.version=${LSMC_RELEASE_SHA:-unknown}" +export LOG_FORMAT="${LOG_FORMAT:-json}" + +exec "$@" diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..6a24122 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2815 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" +resolution-markers = [ + "python_full_version >= '3.15' and sys_platform == 'win32'", + "python_full_version >= '3.15' and sys_platform == 'emscripten'", + "python_full_version >= '3.15' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'win32'", + "python_full_version == '3.14.*' and sys_platform == 'emscripten'", + "python_full_version == '3.14.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "ast-serialize" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/9d/09e27731bd5864a9ce04e3244074e674bb8936bf62b45e0357248717adac/ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6", size = 61157, upload-time = "2026-05-17T17:48:29.429Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/9a/13dde51ba9e15f8b97957ab7cb0120d0e381524d651c6bd630b9c359227f/ast_serialize-0.5.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8f5c14f169eb0972c0c21bada5358b23d6047c76583b005234f865b11f1fa00a", size = 1183520, upload-time = "2026-05-17T17:47:30.831Z" }, + { url = "https://files.pythonhosted.org/packages/37/de/5a7f0a9fe68944f536632a5af84676739c7d2582be42deb082634bf3a754/ast_serialize-0.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7d1a2de9de5be04652f0ed60738356ef94f66db37924a9499fffe98dc491aa0b", size = 1175779, upload-time = "2026-05-17T17:47:32.551Z" }, + { url = "https://files.pythonhosted.org/packages/9c/81/0bb853e76e4f6e9a1855d569003c59e19ffac45f7079d91505d1bb212f92/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be5173fb66f9b49026d9d5a2ff0fc7c7009077107c0eb285b2d60fdf1fe10bd1", size = 1233750, upload-time = "2026-05-17T17:47:34.731Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d3/4cf705beeccc08754d0bbda99aefff26110e209b9a07ac8a6b60eec48531/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8015cd071ac1339924ee2b8098c93e00e155f30a16f40ec9816fcf84f4753f6", size = 1235942, upload-time = "2026-05-17T17:47:36.287Z" }, + { url = "https://files.pythonhosted.org/packages/26/c8/ee097e437ea27dd2b8b227865c875492b585650a5802a22d82b304c8201b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5499e8797edff2a9186aa313ed382c6b422e798e9332d9953badcee6e69a88f2", size = 1442517, upload-time = "2026-05-17T17:47:38.17Z" }, + { url = "https://files.pythonhosted.org/packages/ff/bd/68063442838f1ba68ec72b5436430bc75b3bb17a1a3c3063f09b0c05ae2b/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6848f2a093fb5548751a9a09bff8fcd229e2bbeb0e3331f391b6ae6d26cd9903", size = 1254081, upload-time = "2026-05-17T17:47:39.826Z" }, + { url = "https://files.pythonhosted.org/packages/50/e2/1e520793bc6a4e4524a6ab022391e827825eaa0c3811828bfdc6852eca26/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832d4c998e0b091fd60a6d6bceee535483c4d490de9ba85003af835225719261", size = 1259910, upload-time = "2026-05-17T17:47:41.369Z" }, + { url = "https://files.pythonhosted.org/packages/4e/e1/49b60f467979979cfe6913b43948ff25bca971ad0591d181812f163a988e/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:16db7c62ec0b8efe1d7afd283a388d8f74f2605d56032e5a37747d2de8dba027", size = 1250678, upload-time = "2026-05-17T17:47:43.702Z" }, + { url = "https://files.pythonhosted.org/packages/74/ba/66ab9555de6275677566f6574e5ef6c29cb185ea866f643bc06f8280a8ee/ast_serialize-0.5.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf5eb061eb5bccade4128ad42da33787d72f6013809cd1b590376ece8b3c937", size = 1301603, upload-time = "2026-05-17T17:47:46.256Z" }, + { url = "https://files.pythonhosted.org/packages/66/42/6aca9b9abc710014b2be9059689e5dd1679339e78f567ffb4d255a9e2050/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:104e4a35bd7c124173c41760ef9aaea17ddb3f86c65cb643671d59afbe3ee94c", size = 1410332, upload-time = "2026-05-17T17:47:47.899Z" }, + { url = "https://files.pythonhosted.org/packages/47/68/2f76594432a22581ecf878b5e75a9b8601c24b2241cf0bbeb1e21fcf370c/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:36be371028fc1675acb38a331bde160dbab7ff907fdf00b67eb6911aa106951b", size = 1509979, upload-time = "2026-05-17T17:47:50.942Z" }, + { url = "https://files.pythonhosted.org/packages/40/ac/a93c9b58292653f6c595752f677a08e608f903b710594909e9231a389b3b/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:061ee58bdb52341c8201a6df41182a977736bae3b7ded87ca7176ca25a8a47ab", size = 1505002, upload-time = "2026-05-17T17:47:54.093Z" }, + { url = "https://files.pythonhosted.org/packages/14/2e/b278f68c497ee2f1d1576cbbef8db5281cd4a5f2db040537592ac9c8862e/ast_serialize-0.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15219e9cdc9f53f6f4cb51c009203507228226148c05c5e8fe451c28b435eb3", size = 1456231, upload-time = "2026-05-17T17:47:56.311Z" }, + { url = "https://files.pythonhosted.org/packages/0b/43/419be1c566a4c504cd8fd60ce2f84e790f295495c0f327cfaeadf3d51012/ast_serialize-0.5.0-cp314-cp314t-win32.whl", hash = "sha256:842d1c004bb466c7df036f95fabef789570541922b10976b12f5592a69cf0b38", size = 1058668, upload-time = "2026-05-17T17:47:58.305Z" }, + { url = "https://files.pythonhosted.org/packages/03/6f/c9d4d549295ed05111aeb8853232d1afd9d0a179fddb01eeffbb3a4a6842/ast_serialize-0.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b0c06d760909b095cc466356dfccd05a1c7233a6ca191c020dca2c6a6f16c24c", size = 1101075, upload-time = "2026-05-17T17:48:00.35Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8e/d00c5ab30c58222e07d62956fca86c59d91b9ad32997e633c38b526623a3/ast_serialize-0.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:787baedb0262cc49e8ce37cc15c00ae818e46a165a3b36f5e21ed174998104cb", size = 1075347, upload-time = "2026-05-17T17:48:01.753Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9e/dc2530acb3a60dc6e46d65abf27d1d9f86721694757906a148d90a6860de/ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101", size = 1191380, upload-time = "2026-05-17T17:48:03.738Z" }, + { url = "https://files.pythonhosted.org/packages/26/0a/bd3d18a582f273d6c843d16bb9e22e9e16365ff7991e92f18f798e9f1224/ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a", size = 1183879, upload-time = "2026-05-17T17:48:05.463Z" }, + { url = "https://files.pythonhosted.org/packages/40/ae/1f919100f8620887af58fcc381c61a1f218cdf89c6e155f87b213e61010a/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211", size = 1244529, upload-time = "2026-05-17T17:48:07.008Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ca/6376559dcce707cdbc1d0d9a13c8d3baaaa501e949ce0ebdc4230cd881aa/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf", size = 1240560, upload-time = "2026-05-17T17:48:08.46Z" }, + { url = "https://files.pythonhosted.org/packages/35/b2/a620e206b5aeb7efbf2710336df57d457cffbb3991076bbcc1147ef9abd4/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9", size = 1451172, upload-time = "2026-05-17T17:48:09.922Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e0/4ad5c04c24a40481b2935ce9a0ccdb6023dc8b667167d06ae530cc3512f2/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee", size = 1265072, upload-time = "2026-05-17T17:48:11.469Z" }, + { url = "https://files.pythonhosted.org/packages/b2/71/4d1d479aa56d0101c40e17720c3d6ac2af7269ea0487a80b18e7bfd1a5b7/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809", size = 1270488, upload-time = "2026-05-17T17:48:13.575Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4f/0de1bbe06f6edef9fde4ed12ca8e7b3ec7e6e2bd4e672c5af487f7957665/ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43", size = 1260702, upload-time = "2026-05-17T17:48:15.141Z" }, + { url = "https://files.pythonhosted.org/packages/75/61/e00872439cfdddcc3c1b6cdaa6e5d904ba8e26a18807c67c4e14409d0ca8/ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934", size = 1311182, upload-time = "2026-05-17T17:48:16.779Z" }, + { url = "https://files.pythonhosted.org/packages/76/8e/699a5b955f7926956c95e9e1d74132acad73c2fe7a426f94da89123c20aa/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759", size = 1421410, upload-time = "2026-05-17T17:48:18.527Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ae/d5b7626874478997adc7a29ab28accf21e596fb590c944290401dfd0b29e/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887", size = 1516587, upload-time = "2026-05-17T17:48:20.133Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ce/b59e02a82d9c4244d64cde502e0b00e83e38816abe19155ceb5437402c7f/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27", size = 1515171, upload-time = "2026-05-17T17:48:21.921Z" }, + { url = "https://files.pythonhosted.org/packages/8b/38/d8d90042747d05aa08d4efcf1c99035a5f670a6bf4c214d31644392afbca/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d", size = 1464668, upload-time = "2026-05-17T17:48:23.544Z" }, + { url = "https://files.pythonhosted.org/packages/dd/51/5b840c4df7334104cecffa28f23904fe81ca89ca223d2450e288de39fd3c/ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a", size = 1068311, upload-time = "2026-05-17T17:48:25.027Z" }, + { url = "https://files.pythonhosted.org/packages/41/11/ca5672c7d491825bc4cd6702dea106a6b60d928707712ec257c7833ae476/ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590", size = 1108931, upload-time = "2026-05-17T17:48:26.591Z" }, + { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, +] + +[[package]] +name = "asttokens" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "bandit" +version = "1.9.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "stevedore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/c3/0cb80dfe0f3076e5da7e4c5ad8e57bac6ac357ff4a6406205501cade4965/bandit-1.9.4.tar.gz", hash = "sha256:b589e5de2afe70bd4d53fa0c1da6199f4085af666fde00e8a034f152a52cd628", size = 4242677, upload-time = "2026-02-25T06:44:15.503Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/a4/a26d5b25671d27e03afb5401a0be5899d94ff8fab6a698b1ac5be3ec29ef/bandit-1.9.4-py3-none-any.whl", hash = "sha256:f89ffa663767f5a0585ea075f01020207e966a9c0f2b9ef56a57c7963a3f6f8e", size = 134741, upload-time = "2026-02-25T06:44:13.694Z" }, +] + +[[package]] +name = "black" +version = "26.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/37/5628dd55bf2b34257fc7603f0fe97c40e3aaf24265f416a9c85c95ca1436/black-26.5.1.tar.gz", hash = "sha256:dd321f668053961824bcc1be1cc1df748b2d7e4fa28086b08331e577b0100a73", size = 679439, upload-time = "2026-05-18T16:53:36.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/99/7744b906703228264ef73bdd534df88ec1ef3de45c4e78f6d31b9e32d0c9/black-26.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4ad6fa01f941920f54f2bbb35f3df7673428a0ef98a0b0840c2eaef3b110efa8", size = 2012518, upload-time = "2026-05-18T17:05:20.108Z" }, + { url = "https://files.pythonhosted.org/packages/b7/c0/c5a3b1636dfd09c42534f2b3cf33506814f6d3e066fb0879ffa16c1ae860/black-26.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3915f256e75a2d7cf88d8953d37f780455dc586cc72dee059c528fe77f581217", size = 1816016, upload-time = "2026-05-18T17:05:21.84Z" }, + { url = "https://files.pythonhosted.org/packages/1f/0e/36044316b65ca471d3bb6d3703fd06fb50c6b727c3562f6a5a3153634f88/black-26.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d98d4137277c75dfb898ec8d846c4fd68ba1e9cf77f95e2865c203dc18f4c3d", size = 1884150, upload-time = "2026-05-18T17:05:23.546Z" }, + { url = "https://files.pythonhosted.org/packages/b3/33/dafc5808c2af43672912111d7c3354af1615f7e2be3bed7a878461abbe4d/black-26.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:a1dca32d9f1784af512a13410ec204c6f7f0aa9797a111c42e1c03449821c264", size = 1486825, upload-time = "2026-05-18T17:05:25.004Z" }, + { url = "https://files.pythonhosted.org/packages/82/14/b965ee6ad2a311f28bdbf692def3ee9848d2ae289dab28b27657fcee3e78/black-26.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:1037d5ac7b7b310b2632ad867ec8d0e4c4819dcdb0b820f63135da746a24e418", size = 1288646, upload-time = "2026-05-18T17:05:26.477Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5c/c384363980e11e25ca6b93205949bb331fbf35f4e0dbec376dfa6326cec8/black-26.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b36cf2ddf5566e205f6535f782a62194a184d33e175b64ae8c40b1737522be3", size = 2009020, upload-time = "2026-05-18T17:05:28.132Z" }, + { url = "https://files.pythonhosted.org/packages/0b/df/9f31c5e0babbfed77d505fc5d120beb98b21b33feaeded3924ea941fe360/black-26.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f7ea64ebfa01b50f693508fc39f875e264446d3b097088f84f203b9d09618a0", size = 1813335, upload-time = "2026-05-18T17:05:31.266Z" }, + { url = "https://files.pythonhosted.org/packages/fb/24/8e7b9a2fa61b0afd82209efe937557d180a1fa055bd7f6161eb9defc3719/black-26.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecb3e624844c798144e9bd986954e0adc81d8911a1f30f375e1252fe26e8c294", size = 1881614, upload-time = "2026-05-18T17:05:32.718Z" }, + { url = "https://files.pythonhosted.org/packages/49/ad/b4e0d9365ba8ac34f6bbab62a4b1b2dd5d618fac3fa1b8db968c844201b5/black-26.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:e1a26503279b6b310669fb0b219c39e4820b77e8189fe80f522bb511f247db0a", size = 1488925, upload-time = "2026-05-18T17:05:34.259Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4b/652b859bf5df88a751c30451b09338f7fd26a77d1271c666992f836b7711/black-26.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c34b25da232ead53a6f335b76dbea124f4d152ad568b9080d6f944bc2b34b52", size = 1289883, upload-time = "2026-05-18T17:05:36.019Z" }, + { url = "https://files.pythonhosted.org/packages/a6/16/a8da8eb208c51c7f4ce74609a45d0dcc6d8a2141e45e81ee5289d1bb0d59/black-26.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e88976690a64b0af98312ca958415849cb42423423c5f2ee74af4b49a97a2168", size = 2004800, upload-time = "2026-05-18T17:05:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/11/8a/a479296a19e383b70a725882a6cf3d786540601ff03cabbaaf1cce864c5a/black-26.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32d5ea7f6c8bdfa6e648326ebca1f02b0764e2a029edc6f8dce2627e19d468c3", size = 1815576, upload-time = "2026-05-18T17:05:40.309Z" }, + { url = "https://files.pythonhosted.org/packages/81/6b/cfaf3d39f25132c156a068f6b805576c9103a84086019507c70e1911ee7d/black-26.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea8d16dc41655aa113cd64665e7219446cd7e4ff2248d7178eaa905190c86b18", size = 1877927, upload-time = "2026-05-18T17:05:42.463Z" }, + { url = "https://files.pythonhosted.org/packages/66/76/302e313964bcff7e28df329d39f84f5270095730d85ff0acc260610a0d82/black-26.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:577f21094ea469ef92ec1adaf2c9441a226d2144d01a5be2fa823cecf6543e50", size = 1511860, upload-time = "2026-05-18T17:05:43.943Z" }, + { url = "https://files.pythonhosted.org/packages/27/4e/a3827e35e0e567f9f9ee59e2a0ab979267dca98718f25547ca8c6733afd4/black-26.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:ed1a20af114c301a0269bf01163d51dbef72737fd65f850001e7cbe7f3c7abae", size = 1316632, upload-time = "2026-05-18T17:05:45.521Z" }, + { url = "https://files.pythonhosted.org/packages/94/51/f975cae76d44274cc2868dc9040ac5d58d464784610234455b4e7b19c6ef/black-26.5.1-py3-none-any.whl", hash = "sha256:4ed7f7da04046d2e488437170797d3b4a4ad83906683bcb7dfc68b673bbce5e2", size = 213693, upload-time = "2026-05-18T16:53:33.964Z" }, +] + +[[package]] +name = "bloom-lims" +source = { editable = "." } +dependencies = [ + { name = "bandit" }, + { name = "black" }, + { name = "boto3" }, + { name = "cli-core-yo" }, + { name = "daylily-auth-cognito" }, + { name = "daylily-carrier-tracking" }, + { name = "daylily-tapdb" }, + { name = "djlint" }, + { name = "fastapi" }, + { name = "httpx" }, + { name = "ipython" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "matplotlib" }, + { name = "moto" }, + { name = "mypy" }, + { name = "pandas" }, + { name = "playwright" }, + { name = "pre-commit" }, + { name = "psutil" }, + { name = "psycopg2" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-playwright" }, + { name = "python-multipart" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "requests-mock" }, + { name = "rich" }, + { name = "ruff" }, + { name = "sqlalchemy" }, + { name = "starlette" }, + { name = "uvicorn" }, +] + +[package.metadata] +requires-dist = [ + { name = "bandit", extras = ["toml"], specifier = ">=1.8.0" }, + { name = "black", specifier = ">=24.0.0" }, + { name = "boto3", specifier = ">=1.34.0" }, + { name = "cli-core-yo", specifier = "==2.1.1" }, + { name = "daylily-auth-cognito", specifier = "==2.1.5" }, + { name = "daylily-carrier-tracking", specifier = "==0.5.1" }, + { name = "daylily-tapdb", specifier = ">=7.0.5,<8.0.0" }, + { name = "djlint" }, + { name = "fastapi", specifier = ">=0.110.0" }, + { name = "httpx", specifier = ">=0.27.2" }, + { name = "ipython", specifier = ">=8.18.1" }, + { name = "itsdangerous", specifier = ">=2.1.2" }, + { name = "jinja2", specifier = ">=3.0.3" }, + { name = "matplotlib", specifier = ">=3.8.0" }, + { name = "moto", specifier = ">=5.0.0" }, + { name = "mypy", specifier = ">=1.0" }, + { name = "pandas", specifier = ">=2.2.0" }, + { name = "playwright", specifier = ">=1.42.0" }, + { name = "pre-commit", specifier = ">=3.8.0" }, + { name = "psutil", specifier = ">=5.9.0" }, + { name = "psycopg2", specifier = "==2.9.9" }, + { name = "pydantic", specifier = ">=2.6.0" }, + { name = "pydantic-settings", specifier = ">=2.0.0" }, + { name = "pyjwt", specifier = ">=2.8.0" }, + { name = "pytest", specifier = ">=8.0" }, + { name = "pytest-asyncio", specifier = ">=0.23.0" }, + { name = "pytest-cov", specifier = ">=4.1.0" }, + { name = "pytest-playwright", specifier = ">=0.4.4" }, + { name = "python-multipart", specifier = ">=0.0.9" }, + { name = "pyyaml", specifier = ">=6.0" }, + { name = "requests", specifier = ">=2.28.2" }, + { name = "requests-mock", specifier = ">=1.11.0" }, + { name = "rich", specifier = ">=13.0" }, + { name = "ruff", specifier = ">=0.1.0" }, + { name = "sqlalchemy", specifier = ">=2.0.23" }, + { name = "starlette", specifier = ">=0.35.1" }, + { name = "uvicorn", specifier = ">=0.28.0" }, +] + +[[package]] +name = "boto3" +version = "1.43.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/1f/ef8f2967570c221681056134d02e70ebd490b795c17cf44d52f898344fbd/boto3-1.43.15.tar.gz", hash = "sha256:8dbb1a129c693313218a099d4f086f3bb48f50159b74b5bd8869e1037fde4101", size = 113161, upload-time = "2026-05-26T19:45:14.203Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/8f/373833a5ca86f5b89c7d918c6130aece8d5e43b0c8bdbefac75145abf3f3/boto3-1.43.15-py3-none-any.whl", hash = "sha256:67510ef57a79f0d53b963b0dd1d1741d1e3de1eb2ef6cf1737b6a6bddd34b8cb", size = 140537, upload-time = "2026-05-26T19:45:11.152Z" }, +] + +[[package]] +name = "botocore" +version = "1.43.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/06/4eb6a40f2a5861cd48743765a80e5a6534f26ec0e9884502572cfadeca50/botocore-1.43.15.tar.gz", hash = "sha256:eed2e24962af03ec361a1dc6924f6b2cff0215a0a2f5c690eab655f43d1f700f", size = 15383526, upload-time = "2026-05-26T19:44:57.627Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/34/dfdd494cf22a88be269bcf648d28a65f85dcc0f914f52a02f8e740d6ccaf/botocore-1.43.15-py3-none-any.whl", hash = "sha256:bda4923998d1bf17f8eb6bc767a0923fbb9e525ecd50f839b041e469b46e1c94", size = 15063140, upload-time = "2026-05-26T19:44:54.54Z" }, +] + +[[package]] +name = "certifi" +version = "2026.5.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, + { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, + { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, + { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "cli-core-yo" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/6e/2d5b690639e684483b55633f88a68280b96a87cc00a87e860f784701e31c/cli_core_yo-2.1.1.tar.gz", hash = "sha256:cc73bc220e48051843294f4d4d97067492ce01b41b961a1da23ad6af39153c86", size = 63822, upload-time = "2026-04-27T14:49:18.808Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/15/d7b7e1de008ba81968c56ad6a7e609c72cba38dca284638c720262ce807c/cli_core_yo-2.1.1-py3-none-any.whl", hash = "sha256:c23685086fa2f96ebdcf6fbf64f3a6d7fb96ce8a8a7c262a7f52cadbc2b139e1", size = 36913, upload-time = "2026-04-27T14:49:17.506Z" }, +] + +[[package]] +name = "click" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, +] + +[[package]] +name = "coverage" +version = "7.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132, upload-time = "2026-05-26T20:41:36.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022, upload-time = "2026-05-26T20:39:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379, upload-time = "2026-05-26T20:39:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888, upload-time = "2026-05-26T20:39:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624, upload-time = "2026-05-26T20:39:09.037Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739, upload-time = "2026-05-26T20:39:10.889Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998, upload-time = "2026-05-26T20:39:12.722Z" }, + { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296, upload-time = "2026-05-26T20:39:14.259Z" }, + { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658, upload-time = "2026-05-26T20:39:15.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803, upload-time = "2026-05-26T20:39:17.537Z" }, + { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873, upload-time = "2026-05-26T20:39:19.414Z" }, + { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372, upload-time = "2026-05-26T20:39:21.169Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245, upload-time = "2026-05-26T20:39:23.097Z" }, + { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567, upload-time = "2026-05-26T20:39:24.868Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372, upload-time = "2026-05-26T20:39:26.603Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989, upload-time = "2026-05-26T20:39:28.242Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044, upload-time = "2026-05-26T20:39:29.902Z" }, + { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412, upload-time = "2026-05-26T20:39:31.561Z" }, + { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412, upload-time = "2026-05-26T20:39:33.337Z" }, + { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008, upload-time = "2026-05-26T20:39:35.009Z" }, + { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241, upload-time = "2026-05-26T20:39:36.71Z" }, + { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373, upload-time = "2026-05-26T20:39:38.412Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635, upload-time = "2026-05-26T20:39:40.268Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373, upload-time = "2026-05-26T20:39:42.145Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341, upload-time = "2026-05-26T20:39:43.907Z" }, + { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497, upload-time = "2026-05-26T20:39:46.166Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159, upload-time = "2026-05-26T20:39:48.03Z" }, + { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934, upload-time = "2026-05-26T20:39:49.872Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584, upload-time = "2026-05-26T20:39:51.68Z" }, + { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394, upload-time = "2026-05-26T20:39:53.472Z" }, + { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015, upload-time = "2026-05-26T20:39:55.374Z" }, + { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733, upload-time = "2026-05-26T20:39:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086, upload-time = "2026-05-26T20:39:59.019Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381, upload-time = "2026-05-26T20:40:00.822Z" }, + { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458, upload-time = "2026-05-26T20:40:02.506Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884, upload-time = "2026-05-26T20:40:04.421Z" }, + { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022, upload-time = "2026-05-26T20:40:06.298Z" }, + { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631, upload-time = "2026-05-26T20:40:08.226Z" }, + { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443, upload-time = "2026-05-26T20:40:10.137Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069, upload-time = "2026-05-26T20:40:11.999Z" }, + { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780, upload-time = "2026-05-26T20:40:13.858Z" }, + { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970, upload-time = "2026-05-26T20:40:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157, upload-time = "2026-05-26T20:40:18.382Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259, upload-time = "2026-05-26T20:40:20.381Z" }, + { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320, upload-time = "2026-05-26T20:40:22.316Z" }, + { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577, upload-time = "2026-05-26T20:40:24.894Z" }, + { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091, upload-time = "2026-05-26T20:40:27.249Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421, upload-time = "2026-05-26T20:40:30.084Z" }, + { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466, upload-time = "2026-05-26T20:40:32.542Z" }, + { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973, upload-time = "2026-05-26T20:40:34.473Z" }, + { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318, upload-time = "2026-05-26T20:40:38.154Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633, upload-time = "2026-05-26T20:40:40.164Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488, upload-time = "2026-05-26T20:40:42.631Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329, upload-time = "2026-05-26T20:40:45.208Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291, upload-time = "2026-05-26T20:40:47.502Z" }, + { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564, upload-time = "2026-05-26T20:40:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107, upload-time = "2026-05-26T20:40:51.677Z" }, + { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764, upload-time = "2026-05-26T20:40:54.267Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837, upload-time = "2026-05-26T20:40:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650, upload-time = "2026-05-26T20:40:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218, upload-time = "2026-05-26T20:41:00.321Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822, upload-time = "2026-05-26T20:41:02.281Z" }, + { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084, upload-time = "2026-05-26T20:41:04.593Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454, upload-time = "2026-05-26T20:41:06.641Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578, upload-time = "2026-05-26T20:41:08.556Z" }, + { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981, upload-time = "2026-05-26T20:41:10.824Z" }, + { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112, upload-time = "2026-05-26T20:41:12.966Z" }, + { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558, upload-time = "2026-05-26T20:41:15Z" }, + { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447, upload-time = "2026-05-26T20:41:17.369Z" }, + { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048, upload-time = "2026-05-26T20:41:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781, upload-time = "2026-05-26T20:41:21.559Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896, upload-time = "2026-05-26T20:41:23.428Z" }, + { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214, upload-time = "2026-05-26T20:41:25.419Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624, upload-time = "2026-05-26T20:41:27.795Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728, upload-time = "2026-05-26T20:41:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752, upload-time = "2026-05-26T20:41:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815, upload-time = "2026-05-26T20:41:34.078Z" }, +] + +[[package]] +name = "cryptography" +version = "48.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9f/a9/db8f313fdcd85d767d4973515e1db101f9c71f95fced83233de224673757/cryptography-48.0.0.tar.gz", hash = "sha256:5c3932f4436d1cccb036cb0eaef46e6e2db91035166f1ad6505c3c9d5a635920", size = 832984, upload-time = "2026-05-04T22:59:38.133Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/3d/01f6dd9190170a5a241e0e98c2d04be3664a9e6f5b9b872cde63aff1c3dd/cryptography-48.0.0-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:0c558d2cdffd8f4bbb30fc7134c74d2ca9a476f830bb053074498fbc86f41ed6", size = 8001587, upload-time = "2026-05-04T22:57:36.803Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6e/e90527eef33f309beb811cf7c982c3aeffcce8e3edb178baa4ca3ae4a6fa/cryptography-48.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5333311663ea94f75dd408665686aaf426563556bb5283554a3539177e03b8c", size = 4690433, upload-time = "2026-05-04T22:57:40.373Z" }, + { url = "https://files.pythonhosted.org/packages/90/04/673510ed51ddff56575f306cf1617d80411ee76831ccd3097599140efdfe/cryptography-48.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7995ef305d7165c3f11ae07f2517e5a4f1d5c18da1376a0a9ed496336b69e5f3", size = 4710620, upload-time = "2026-05-04T22:57:42.935Z" }, + { url = "https://files.pythonhosted.org/packages/14/d5/e9c4ef932c8d800490c34d8bd589d64a31d5890e27ec9e9ad532be893294/cryptography-48.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:40ba1f85eaa6959837b1d51c9767e230e14612eea4ef110ee8854ada22da1bf5", size = 4696283, upload-time = "2026-05-04T22:57:45.294Z" }, + { url = "https://files.pythonhosted.org/packages/0c/29/174b9dfb60b12d59ecfc6cfa04bc88c21b42a54f01b8aae09bb6e51e4c7f/cryptography-48.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:369a6348999f94bbd53435c894377b20ab95f25a9065c283570e70150d8abc3c", size = 5296573, upload-time = "2026-05-04T22:57:47.933Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/0d29a6fd7d0d1373f0c0c88a04ba20e359b257753ac497564cd660fc1d55/cryptography-48.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a0e692c683f4df67815a2d258b324e66f4738bd7a96a218c826dce4f4bd05d8f", size = 4743677, upload-time = "2026-05-04T22:57:50.067Z" }, + { url = "https://files.pythonhosted.org/packages/30/be/eef653013d5c63b6a490529e0316f9ac14a37602965d4903efed1399f32b/cryptography-48.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:18349bbc56f4743c8b12dc32e2bccb2cf83ee8b69a3bba74ef8ae857e26b3d25", size = 4330808, upload-time = "2026-05-04T22:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/84/9e/500463e87abb7a0a0f9f256ec21123ecde0a7b5541a15e840ea54551fd81/cryptography-48.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e8eac43dfca5c4cccc6dad9a80504436fca53bb9bc3100a2386d730fbe6b602", size = 4695941, upload-time = "2026-05-04T22:57:54.603Z" }, + { url = "https://files.pythonhosted.org/packages/e3/dc/7303087450c2ec9e7fbb750e17c2abfbc658f23cbd0e54009509b7cc4091/cryptography-48.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9ccdac7d40688ecb5a3b4a604b8a88c8002e3442d6c60aead1db2a89a041560c", size = 5252579, upload-time = "2026-05-04T22:57:57.207Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c0/7101d3b7215edcdc90c45da544961fd8ed2d6448f77577460fa75a8443f7/cryptography-48.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:bd72e68b06bb1e96913f97dd4901119bc17f39d4586a5adf2d3e47bc2b9d58b5", size = 4743326, upload-time = "2026-05-04T22:57:59.535Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/5b833bad13016f562ab9d063d68199a4bd121d18458e439515601d3357ec/cryptography-48.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:59baa2cb386c4f0b9905bd6eb4c2a79a69a128408fd31d32ca4d7102d4156321", size = 4826672, upload-time = "2026-05-04T22:58:01.996Z" }, + { url = "https://files.pythonhosted.org/packages/98/e1/7074eb8bf3c135558c73fc2bcf0f5633f912e6fb87e868a55c454080ef09/cryptography-48.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9249e3cd978541d665967ac2cb2787fd6a62bddf1e75b3e347a594d7dacf4f74", size = 4972574, upload-time = "2026-05-04T22:58:03.968Z" }, + { url = "https://files.pythonhosted.org/packages/04/70/e5a1b41d325f797f39427aa44ef8baf0be500065ab6d8e10369d850d4a4f/cryptography-48.0.0-cp311-abi3-win32.whl", hash = "sha256:9c459db21422be75e2809370b829a87eb37f74cd785fc4aa9ea1e5f43b47cda4", size = 3294868, upload-time = "2026-05-04T22:58:06.467Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ac/8ac51b4a5fc5932eb7ee5c517ba7dc8cd834f0048962b6b352f00f41ebf9/cryptography-48.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:5b012212e08b8dd5edc78ef54da83dd9892fd9105323b3993eff6bea65dc21d7", size = 3817107, upload-time = "2026-05-04T22:58:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/6b/84/70e3feea9feea87fd7cbe77efb2712ae1e3e6edf10749dc6e95f4e60e455/cryptography-48.0.0-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:3cb07a3ed6431663cd321ea8a000a1314c74211f823e4177fefa2255e057d1ec", size = 7986556, upload-time = "2026-05-04T22:58:11.172Z" }, + { url = "https://files.pythonhosted.org/packages/89/6e/18e07a618bb5442ba10cf4df16e99c071365528aa570dfcb8c02e25a303b/cryptography-48.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8c7378637d7d88016fa6791c159f698b3d3eed28ebf844ac36b9dc04a14dae18", size = 4684776, upload-time = "2026-05-04T22:58:13.712Z" }, + { url = "https://files.pythonhosted.org/packages/be/6a/4ea3b4c6c6759794d5ee2103c304a5076dc4b19ae1f9fe47dba439e159e9/cryptography-48.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc90c0b39b2e3c65ef52c804b72e3c58f8a04ab2a1871272798e5f9572c17d20", size = 4698121, upload-time = "2026-05-04T22:58:16.448Z" }, + { url = "https://files.pythonhosted.org/packages/2f/59/6ff6ad6cae03bb887da2a5860b2c9805f8dac969ef01ce563336c49bd1d1/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:76341972e1eff8b4bea859f09c0d3e64b96ce931b084f9b9b7db8ef364c30eff", size = 4690042, upload-time = "2026-05-04T22:58:18.544Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/fc334ed8cfd705aca282fe4d8f5ae64a8e0f74932e9feecb344610cf6e4d/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:55b7718303bf06a5753dcdccf2f3945cf18ad7bffde41b61226e4db31ab89a9c", size = 5282526, upload-time = "2026-05-04T22:58:20.75Z" }, + { url = "https://files.pythonhosted.org/packages/11/08/9f8c5386cc4cd90d8255c7cdd0f5baf459a08502a09de30dc51f553d38dc/cryptography-48.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:a64697c641c7b1b2178e573cbc31c7c6684cd56883a478d75143dbb7118036db", size = 4733116, upload-time = "2026-05-04T22:58:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/b8/77/99307d7574045699f8805aa500fa0fb83422d115b5400a064ddd306d7750/cryptography-48.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:561215ea3879cb1cbbf272867e2efda62476f240fb58c64de6b393ae19246741", size = 4316030, upload-time = "2026-05-04T22:58:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/fd/36/a608b98337af3cb2aff4818e406649d30572b7031918b04c87d979495348/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ad64688338ed4bc1a6618076ba75fd7194a5f1797ac60b47afe926285adb3166", size = 4689640, upload-time = "2026-05-04T22:58:27.747Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a6/825010a291b4438aecc1f568bc428189fc1175515223632477c07dc0a6df/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:906cbf0670286c6e0044156bc7d4af9cbb0ef6db9f73e52c3ec56ba6bdde5336", size = 5237657, upload-time = "2026-05-04T22:58:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/b9/09/4e76a09b4caa29aad535ddc806f5d4c5d01885bd978bd984fbc6ca032cae/cryptography-48.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:ea8990436d914540a40ab24b6a77c0969695ed52f4a4874c5137ccf7045a7057", size = 4732362, upload-time = "2026-05-04T22:58:32.009Z" }, + { url = "https://files.pythonhosted.org/packages/18/78/444fa04a77d0cb95f417dda20d450e13c56ba8e5220fc892a1658f44f882/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c18684a7f0cc9a3cb60328f496b8e3372def7c5d2df39ac267878b05565aaaae", size = 4819580, upload-time = "2026-05-04T22:58:34.254Z" }, + { url = "https://files.pythonhosted.org/packages/38/85/ea67067c70a1fd4be2c63d35eeed82658023021affccc7b17705f8527dd2/cryptography-48.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9be5aafa5736574f8f15f262adc81b2a9869e2cfe9014d52a44633905b40d52c", size = 4963283, upload-time = "2026-05-04T22:58:36.376Z" }, + { url = "https://files.pythonhosted.org/packages/75/54/cc6d0f3deac3e81c7f847e8a189a12b6cdd65059b43dad25d4316abd849a/cryptography-48.0.0-cp314-cp314t-win32.whl", hash = "sha256:c17dfe85494deaeddc5ce251aebd1d60bbe6afc8b62071bb0b469431a000124f", size = 3270954, upload-time = "2026-05-04T22:58:38.791Z" }, + { url = "https://files.pythonhosted.org/packages/49/67/cc947e288c0758a4e5473d1dcb743037ab7785541265a969240b8885441a/cryptography-48.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27241b1dc9962e056062a8eef1991d02c3a24569c95975bd2322a8a52c6e5e12", size = 3797313, upload-time = "2026-05-04T22:58:40.746Z" }, + { url = "https://files.pythonhosted.org/packages/f2/63/61d4a4e1c6b6bab6ce1e213cd36a24c415d90e76d78c5eb8577c5541d2e8/cryptography-48.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:58d00498e8933e4a194f3076aee1b4a97dfec1a6da444535755822fe5d8b0b86", size = 7983482, upload-time = "2026-05-04T22:58:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ac/f5b5995b87770c693e2596559ffafe195b4033a57f14a82268a2842953f3/cryptography-48.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:614d0949f4790582d2cc25553abd09dd723025f0c0e7c67376a1d77196743d6e", size = 4683266, upload-time = "2026-05-04T22:58:46.064Z" }, + { url = "https://files.pythonhosted.org/packages/ec/c6/8b14f67e18338fbc4adb76f66c001f5c3610b3e2d1837f268f47a347dbbb/cryptography-48.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ce4bfae76319a532a2dc68f82cc32f5676ee792a983187dac07183690e5c66f", size = 4696228, upload-time = "2026-05-04T22:58:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/f808fbae9514bd91b47875b003f13e284c8c6bdfd904b7944e803937eec1/cryptography-48.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2eb992bbd4661238c5a397594c83f5b4dc2bc5b848c365c8f991b6780efcc5c7", size = 4689097, upload-time = "2026-05-04T22:58:50.9Z" }, + { url = "https://files.pythonhosted.org/packages/93/01/d86632d7d28db8ae83221995752eeb6639ffb374c2d22955648cf8d52797/cryptography-48.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:22a5cb272895dce158b2cacdfdc3debd299019659f42947dbdac6f32d68fe832", size = 5283582, upload-time = "2026-05-04T22:58:53.017Z" }, + { url = "https://files.pythonhosted.org/packages/02/e1/50edc7a50334807cc4791fc4a0ce7468b4a1416d9138eab358bfc9a3d70b/cryptography-48.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2b4d59804e8408e2fea7d1fbaf218e5ec984325221db76e6a241a9abd6cdd95c", size = 4730479, upload-time = "2026-05-04T22:58:55.611Z" }, + { url = "https://files.pythonhosted.org/packages/6f/af/99a582b1b1641ff5911ac559beb45097cf79efd4ead4657f578ef1af2d47/cryptography-48.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:984a20b0f62a26f48a3396c72e4bc34c66e356d356bf370053066b3b6d54634a", size = 4326481, upload-time = "2026-05-04T22:58:57.607Z" }, + { url = "https://files.pythonhosted.org/packages/90/ee/89aa26a06ef0a7d7611788ffd571a7c50e368cc6a4d5eef8b4884e866edb/cryptography-48.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5a5ed8fde7a1d09376ca0b40e68cd59c69fe23b1f9768bd5824f54681626032a", size = 4688713, upload-time = "2026-05-04T22:59:00.077Z" }, + { url = "https://files.pythonhosted.org/packages/70/ba/bcb1b0bb7a33d4c7c0c4d4c7874b4a62ae4f56113a5f4baefa362dfb1f0f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:8cd666227ef7af430aa5914a9910e0ddd703e75f039cef0825cd0da71b6b711a", size = 5238165, upload-time = "2026-05-04T22:59:02.317Z" }, + { url = "https://files.pythonhosted.org/packages/c9/70/ca4003b1ce5ca3dc3186ada51908c8a9b9ff7d5cab83cc0d43ee14ec144f/cryptography-48.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9071196d81abc88b3516ac8cdfad32e2b66dd4a5393a8e68a961e9161ddc6239", size = 4729947, upload-time = "2026-05-04T22:59:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/4ec7cf774207905aef1a8d11c3750d5a1db805eb380ee4e16df317870128/cryptography-48.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1e2d54c8be6152856a36f0882ab231e70f8ec7f14e93cf87db8a2ed056bf160c", size = 4822059, upload-time = "2026-05-04T22:59:07.802Z" }, + { url = "https://files.pythonhosted.org/packages/1e/75/a2e55f99c16fcac7b5d6c1eb19ad8e00799854d6be5ca845f9259eae1681/cryptography-48.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a5da777e32ffed6f85a7b2b3f7c5cbc88c146bfcd0a1d7baf5fcc6c52ee35dd4", size = 4960575, upload-time = "2026-05-04T22:59:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/b8/23/6e6f32143ab5d8b36ca848a502c4bcd477ae75b9e1677e3530d669062578/cryptography-48.0.0-cp39-abi3-win32.whl", hash = "sha256:77a2ccbbe917f6710e05ba9adaa25fb5075620bf3ea6fb751997875aff4ae4bd", size = 3279117, upload-time = "2026-05-04T22:59:12.019Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, +] + +[[package]] +name = "cssbeautifier" +version = "1.15.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "editorconfig" }, + { name = "jsbeautifier" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/01/fdf41c1e5f93d359681976ba10410a04b299d248e28ecce1d4e88588dde4/cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5", size = 25376, upload-time = "2025-02-27T17:53:51.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/51/ef6c5628e46092f0a54c7cee69acc827adc6b6aab57b55d344fefbdf28f1/cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98", size = 123667, upload-time = "2025-02-27T17:53:43.594Z" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "daylily-auth-cognito" +version = "2.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "boto3" }, + { name = "cli-core-yo" }, + { name = "fastapi" }, + { name = "itsdangerous" }, + { name = "python-jose", extra = ["cryptography"] }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/02/af8947a387570c09bc4b32c93935619a3bb742ff2505b0b44ccf8ffa9337/daylily_auth_cognito-2.1.5.tar.gz", hash = "sha256:407b73e22c72935652471c10a35481a7f415cc4c16aa3430632152cb2d3bf44a", size = 77340, upload-time = "2026-04-27T18:04:22.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/8d/bac93d358a84a2c288a82dfb9480781597721992942063d51bfdacb564d4/daylily_auth_cognito-2.1.5-py3-none-any.whl", hash = "sha256:77499606791b90bfbe3a85a32f0dd0b70c70c085cd26fe33a15771baf1485081", size = 49918, upload-time = "2026-04-27T18:04:21.345Z" }, +] + +[[package]] +name = "daylily-carrier-tracking" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, + { name = "yaml-config-day" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/10/0fbba9a39ab00c7d39456d145abe612b939aa4931d9aab4d54c782f9b1a2/daylily_carrier_tracking-0.5.1.tar.gz", hash = "sha256:86f8085b44db8bb85c879b93e9be7fdb65c610e37e6eb2c9dc2aedeea572350f", size = 34456, upload-time = "2026-03-10T18:08:04.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/72/b028cc143146bfb091053fa8ab2402eca35d498f7be5dcd5dfdcbc44ada2/daylily_carrier_tracking-0.5.1-py3-none-any.whl", hash = "sha256:004884fdf7295a1f37a06f76c3a26503d4c000d3fdcdb9a2aea08d13113c3f07", size = 22032, upload-time = "2026-03-10T18:08:02.438Z" }, +] + +[[package]] +name = "daylily-tapdb" +version = "7.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cli-core-yo" }, + { name = "jsonschema" }, + { name = "meridian-euid" }, + { name = "psycopg2-binary" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "rich" }, + { name = "sqlalchemy" }, + { name = "typer" }, + { name = "uuid6" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/f0/e43d08ff8dd476e7b162408b58680fbc3d3c82e3b89119d5457f303d6bac/daylily_tapdb-7.0.5.tar.gz", hash = "sha256:51fa260efc3c57a67496d0c7cdebca72a51ca28ee4156fe14744d62d5f6ea2ae", size = 375380, upload-time = "2026-05-18T14:02:42.708Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/6c/f5b3f2143076144fa8315acd839205bcd29790980136ac232abe5f1bd5da/daylily_tapdb-7.0.5-py3-none-any.whl", hash = "sha256:6cad6a5ec40337c4fa9fb05e7e871c082d47ad06ee78eb1a7005da30aeef7f07", size = 254541, upload-time = "2026-05-18T14:02:40.958Z" }, +] + +[[package]] +name = "decorator" +version = "5.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/60/8b/32f9823da46cde7df2087faa08cd98d01b908f8dcab982cdba9c84e85355/decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82", size = 58084, upload-time = "2026-05-18T06:03:28.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c", size = 10365, upload-time = "2026-05-18T06:03:26.517Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "djlint" +version = "1.36.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama" }, + { name = "cssbeautifier" }, + { name = "jsbeautifier" }, + { name = "json5" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/89/ecf5be9f5c59a0c53bcaa29671742c5e269cc7d0e2622e3f65f41df251bf/djlint-1.36.4.tar.gz", hash = "sha256:17254f218b46fe5a714b224c85074c099bcb74e3b2e1f15c2ddc2cf415a408a1", size = 47849, upload-time = "2024-12-24T13:06:36.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/f5/9ae02b875604755d4d00cebf96b218b0faa3198edc630f56a139581aed87/djlint-1.36.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff9faffd7d43ac20467493fa71d5355b5b330a00ade1c4d1e859022f4195223b", size = 354886, upload-time = "2024-12-24T13:06:11.571Z" }, + { url = "https://files.pythonhosted.org/packages/97/51/284443ff2f2a278f61d4ae6ae55eaf820ad9f0fd386d781cdfe91f4de495/djlint-1.36.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:79489e262b5ac23a8dfb7ca37f1eea979674cfc2d2644f7061d95bea12c38f7e", size = 323237, upload-time = "2024-12-24T13:06:13.057Z" }, + { url = "https://files.pythonhosted.org/packages/6d/5e/791f4c5571f3f168ad26fa3757af8f7a05c623fde1134a9c4de814ee33b7/djlint-1.36.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e58c5fa8c6477144a0be0a87273706a059e6dd0d6efae01146ae8c29cdfca675", size = 411719, upload-time = "2024-12-24T13:06:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/1f/11/894425add6f84deffcc6e373f2ce250f2f7b01aa58c7f230016ebe7a0085/djlint-1.36.4-cp312-cp312-win_amd64.whl", hash = "sha256:bb6903777bf3124f5efedcddf1f4716aef097a7ec4223fc0fa54b865829a6e08", size = 362076, upload-time = "2024-12-24T13:06:17.517Z" }, + { url = "https://files.pythonhosted.org/packages/da/83/88b4c885812921739f5529a29085c3762705154d41caf7eb9a8886a3380c/djlint-1.36.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ead475013bcac46095b1bbc8cf97ed2f06e83422335734363f8a76b4ba7e47c2", size = 354384, upload-time = "2024-12-24T13:06:20.809Z" }, + { url = "https://files.pythonhosted.org/packages/32/38/67695f7a150b3d9d62fadb65242213d96024151570c3cf5d966effa68b0e/djlint-1.36.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6c601dfa68ea253311deb4a29a7362b7a64933bdfcfb5a06618f3e70ad1fa835", size = 322971, upload-time = "2024-12-24T13:06:22.185Z" }, + { url = "https://files.pythonhosted.org/packages/ac/7a/cd851393291b12e7fe17cf5d4d8874b8ea133aebbe9235f5314aabc96a52/djlint-1.36.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda5014f295002363381969864addeb2db13955f1b26e772657c3b273ed7809f", size = 410972, upload-time = "2024-12-24T13:06:24.077Z" }, + { url = "https://files.pythonhosted.org/packages/6c/31/56469120394b970d4f079a552fde21ed27702ca729595ab0ed459eb6d240/djlint-1.36.4-cp313-cp313-win_amd64.whl", hash = "sha256:16ce37e085afe5a30953b2bd87cbe34c37843d94c701fc68a2dda06c1e428ff4", size = 362053, upload-time = "2024-12-24T13:06:25.432Z" }, + { url = "https://files.pythonhosted.org/packages/4b/67/f7aeea9be6fb3bd984487af8d0d80225a0b1e5f6f7126e3332d349fb13fe/djlint-1.36.4-py3-none-any.whl", hash = "sha256:e9699b8ac3057a6ed04fb90835b89bee954ed1959c01541ce4f8f729c938afdd", size = 52290, upload-time = "2024-12-24T13:06:33.76Z" }, +] + +[[package]] +name = "ecdsa" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/25/ca/8de7744cb3bc966c85430ca2d0fcaeea872507c6a4cf6e007f7fe269ed9d/ecdsa-0.19.2.tar.gz", hash = "sha256:62635b0ac1ca2e027f82122b5b81cb706edc38cd91c63dda28e4f3455a2bf930", size = 202432, upload-time = "2026-03-26T09:58:17.675Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/79/119091c98e2bf49e24ed9f3ae69f816d715d2904aefa6a2baa039a2ba0b0/ecdsa-0.19.2-py2.py3-none-any.whl", hash = "sha256:840f5dc5e375c68f36c1a7a5b9caad28f95daa65185c9253c0c08dd952bb7399", size = 150818, upload-time = "2026-03-26T09:58:15.808Z" }, +] + +[[package]] +name = "editorconfig" +version = "0.17.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/3a/a61d9a1f319a186b05d14df17daea42fcddea63c213bcd61a929fb3a6796/editorconfig-0.17.1.tar.gz", hash = "sha256:23c08b00e8e08cc3adcddb825251c497478df1dada6aefeb01e626ad37303745", size = 14695, upload-time = "2025-06-09T08:21:37.097Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/fd/a40c621ff207f3ce8e484aa0fc8ba4eb6e3ecf52e15b42ba764b457a9550/editorconfig-0.17.1-py3-none-any.whl", hash = "sha256:1eda9c2c0db8c16dbd50111b710572a5e6de934e39772de1959d41f64fc17c82", size = 16360, upload-time = "2025-06-09T08:21:35.654Z" }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, +] + +[[package]] +name = "fastapi" +version = "0.136.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/82/45359b62a067409bd929ae8a56b8ed13e5a8c8a61194b3c236920999ab83/fastapi-0.136.3-py3-none-any.whl", hash = "sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620", size = 117481, upload-time = "2026-05-23T18:53:16.924Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, +] + +[[package]] +name = "fonttools" +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" }, + { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" }, + { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" }, + { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" }, + { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" }, + { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" }, + { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338, upload-time = "2026-05-14T12:03:50.052Z" }, + { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661, upload-time = "2026-05-14T12:03:52.154Z" }, + { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526, upload-time = "2026-05-14T12:03:54.647Z" }, + { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946, upload-time = "2026-05-14T12:03:56.984Z" }, + { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489, upload-time = "2026-05-14T12:03:59.443Z" }, + { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870, upload-time = "2026-05-14T12:04:02.122Z" }, + { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026, upload-time = "2026-05-14T12:04:04.47Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454, upload-time = "2026-05-14T12:04:06.752Z" }, + { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152, upload-time = "2026-05-14T12:04:08.706Z" }, + { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809, upload-time = "2026-05-14T12:04:10.783Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649, upload-time = "2026-05-14T12:04:12.747Z" }, + { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147, upload-time = "2026-05-14T12:04:14.806Z" }, + { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237, upload-time = "2026-05-14T12:04:17.675Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933, upload-time = "2026-05-14T12:04:20.818Z" }, + { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326, upload-time = "2026-05-14T12:04:24.22Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829, upload-time = "2026-05-14T12:04:26.829Z" }, + { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, +] + +[[package]] +name = "greenlet" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356, upload-time = "2026-05-20T15:05:03.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220, upload-time = "2026-05-20T13:07:28.463Z" }, + { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585, upload-time = "2026-05-20T14:00:06.141Z" }, + { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215, upload-time = "2026-05-20T14:05:42.675Z" }, + { url = "https://files.pythonhosted.org/packages/7c/6c/de5b1b388cd2d9fbdfeab324863daba37d54e6e233ddbefd70b385a8c591/greenlet-3.5.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:89101bfd5011e069be974903cb3a4e4523845e4ece2d62dcd8d358933c0ef249", size = 620094, upload-time = "2026-05-20T14:09:09.18Z" }, + { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358, upload-time = "2026-05-20T13:14:26.37Z" }, + { url = "https://files.pythonhosted.org/packages/4a/43/1204baffab8a6476464795a7ccf394a3248d4f22c9f87173a15b36b6d971/greenlet-3.5.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:e6cd99ea59dd5d89f0c956606571d79bfe6f68c9eb7f4a4083a41a7f1587edee", size = 422782, upload-time = "2026-05-20T14:01:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475, upload-time = "2026-05-20T14:02:25.29Z" }, + { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625, upload-time = "2026-05-20T13:14:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791, upload-time = "2026-05-20T13:10:39.018Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580, upload-time = "2026-05-20T13:08:45.056Z" }, + { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060, upload-time = "2026-05-20T13:08:51.899Z" }, + { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370, upload-time = "2026-05-20T14:00:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987, upload-time = "2026-05-20T14:05:44.237Z" }, + { url = "https://files.pythonhosted.org/packages/19/ba/c24110c55dffa55aa6e1d98b45310da33801aeba7686ff0190fe5d46fd32/greenlet-3.5.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d40a890035c0058cadbdc4af7569800fd28a0e527a0fdbb7b5f9418f176846ce", size = 622911, upload-time = "2026-05-20T14:09:10.598Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911, upload-time = "2026-05-20T13:14:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/ec/7b/d20db2e8a5ad6c038702f3179b136f93f0a3d1a21a0c0777f3e470cdf4b2/greenlet-3.5.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:67821bb03e4e98664490edb787ff6af501194c29bbee0f5c1dfdcf1dc3d9d436", size = 425228, upload-time = "2026-05-20T14:01:40.837Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689, upload-time = "2026-05-20T14:02:27.225Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602, upload-time = "2026-05-20T13:14:36.344Z" }, + { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683, upload-time = "2026-05-20T13:11:50.57Z" }, + { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499, upload-time = "2026-05-20T13:12:42.028Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cb/c62454606daf5640369c94d8a9dd540599b1bfc090e2d2180cb77f4038d2/greenlet-3.5.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d8ab31c9de8651a2facdd5c5bb0011f2380dd1a7af78ce2adf4b56095294fc07", size = 285579, upload-time = "2026-05-20T13:08:56.396Z" }, + { url = "https://files.pythonhosted.org/packages/ec/71/c4270398c2eba968a6071af1dfbdcaeee6ec1c24bc8b435b8cc452700da6/greenlet-3.5.1-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e300185139abc337ade480c327183adf42a875ac7181bfe66d7d4efea31fbea", size = 651106, upload-time = "2026-05-20T14:00:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ab/71e34b78a44ec271fb5f550c17bc46d301ddc5953890d935f270b0dcdb5a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7ffdb990dcaa0234cf9845aead5df2e3c3a8b6507d409274dd87e0d5ab05ffc2", size = 663478, upload-time = "2026-05-20T14:05:45.88Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/2d80842910da44f78c286532d084b8a5c3717c844ae80ceb3858738ae89a/greenlet-3.5.1-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c09df69dc1712d131332054a858a3e5cca400967fa3a672e2324fbb0971448c", size = 667767, upload-time = "2026-05-20T14:09:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/96/4efd6fa5c62c85426a0c19077a586258ebc3a2a146ff2493e4312a697a22/greenlet-3.5.1-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2f82b3597e9d83b63408affed0b48fd0f54935edac4302237b9a837be0dae33c", size = 660800, upload-time = "2026-05-20T13:14:29.129Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d3/dad2eecedfbb1ed7050a20dcfae40c1442b74bc7423608be2c7e03ee7133/greenlet-3.5.1-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:a4764e0bfc6a4d114c865b32520805c16a990ef5f286a514413b05d5ecd6a23d", size = 470786, upload-time = "2026-05-20T14:01:42.064Z" }, + { url = "https://files.pythonhosted.org/packages/7a/e0/6c71401a25cac7000261304e866a2f2cc04dc74810d40e2f118aa4799495/greenlet-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c0141e37414c10164e702b8fb1473304221ad98f71600850c6ef7ff4880feba0", size = 1617518, upload-time = "2026-05-20T14:02:28.662Z" }, + { url = "https://files.pythonhosted.org/packages/41/26/c5c06643e8c0af9e7bf18e16cb51d0ab7625155f0392e1c9015d66d556cd/greenlet-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:50ae25a67bea74ea41fb14b960bc532df73eb713417b2d61892dced82fe8d3bc", size = 1681593, upload-time = "2026-05-20T13:14:39.417Z" }, + { url = "https://files.pythonhosted.org/packages/8a/bd/e11a108317485075e68af9d23039619b86b28130c3b50d227d42edece64b/greenlet-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:8a17c42330e261299766b75ac1ea32caa437a9453c8f65d16a13140db378ecd3", size = 239800, upload-time = "2026-05-20T13:09:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/47/f8/8e8e8417b7bf28639a5a56356ef934d0375e1d0c70a57e04d7701e870ffe/greenlet-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:7b5f5fae05b8ac6d176a61b60c394a8cbdc2b5b91b81793066e68745cf165e54", size = 236862, upload-time = "2026-05-20T13:09:10.498Z" }, + { url = "https://files.pythonhosted.org/packages/90/12/41bf27fde4d3605d3773ae57751eda182b8be2f5398011c041173b1d9534/greenlet-3.5.1-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:ea8da1e900d758d078810d4255d8c6aa572181896a31ec79d779eb79c3adc9ad", size = 293637, upload-time = "2026-05-20T13:12:35.529Z" }, + { url = "https://files.pythonhosted.org/packages/44/44/ba14b23e9757707050c2f397d305bbcae62e5d7cad122f8b6baec5ae4a1f/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a19570c52a21420dcbc94e661994bc325c0b5b11304540fed514586da5dc8f2e", size = 650840, upload-time = "2026-05-20T14:00:11.079Z" }, + { url = "https://files.pythonhosted.org/packages/a8/37/5ddc2b686a6844f91abecef43411842426da2e1573f60b49ecf2547f4ae1/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d955c89b75eeca4723d7cc14135f393cd47c32e2a6cb4a8e4c6e760a26b0986", size = 656416, upload-time = "2026-05-20T14:05:47.118Z" }, + { url = "https://files.pythonhosted.org/packages/8c/46/5987dcd1a2570ba84f3b187536b2ca3ae97613387e57f5cfa99df068fe5e/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea37d5a157eb9493820d3792ac4ece28619a394391d2b9f2f78057d396ff0f0f", size = 656607, upload-time = "2026-05-20T14:09:13.949Z" }, + { url = "https://files.pythonhosted.org/packages/e1/f0/d17510297c35a2992712f0bf84de3779749999f7d3d63aa1f09db7c62dbe/greenlet-3.5.1-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2daaaebd1a5aa88c49045b6baf9310b3263796bd88db713edf37cf53e7bb4e", size = 654397, upload-time = "2026-05-20T13:14:30.696Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c1/6da0a9ddcc29d7e51ef14883fa3dc1e53b3f4ffba00582106c7bf55da1d8/greenlet-3.5.1-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:8d8a23250ea3ec7b36de8fa4b541e9e2db3ee82915cc060ab0631609ad8b28de", size = 488287, upload-time = "2026-05-20T14:01:43.143Z" }, + { url = "https://files.pythonhosted.org/packages/37/eb/147387705bb89092645b012586e7273cb5ed3c90ef7eaf3a69173eaf0209/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbd69cc349e43bf3a8ae1c85548ff0718efc887615c2db16c3833d7b0b072d", size = 1614469, upload-time = "2026-05-20T14:02:30.192Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4e/37ee0da7732b7aa9896f17e15579a9df34b9fcb9dd494f0adfa749af6623/greenlet-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4378720dd888136c27215a0214d32a4d37c3852765d45bc37aad0623423cfd78", size = 1675115, upload-time = "2026-05-20T13:14:40.972Z" }, + { url = "https://files.pythonhosted.org/packages/57/f3/97dfcf4a6eb5077f8a672234216fb5923eb89f2cab7081cb10b2cf75b605/greenlet-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:45718441607f9325d948db98cbc691276059316d0358c188c246da4e1d4d23d2", size = 245246, upload-time = "2026-05-20T13:12:22.646Z" }, + { url = "https://files.pythonhosted.org/packages/5d/73/d7f72e34b582f694f4a9b248162db7b09cc458a259ba8f0c0bfa1a34ea7d/greenlet-3.5.1-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:2baee5ca02031757ffe8cc3d69f0cc0aec7065ce362622da74f32d3bcab1c541", size = 285575, upload-time = "2026-05-20T13:12:07.043Z" }, + { url = "https://files.pythonhosted.org/packages/df/59/fa9c6e87dc8ad27a95dabe2f29f372b733d05a8a67470f6c901ed9975655/greenlet-3.5.1-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b1ec3274918a81d3ea778b9e75b56b72b33f300edb6cf7f3a7fe1dae56683de", size = 656428, upload-time = "2026-05-20T14:00:12.556Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f9/e753408871eaa61dfe35e619cfc67512b036fde99893685d50eea9e07146/greenlet-3.5.1-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:111e2390ffffc47d5840b01711dd7fac07d4c09283d0283e7f3264b14e284c64", size = 667064, upload-time = "2026-05-20T14:05:48.662Z" }, + { url = "https://files.pythonhosted.org/packages/dc/74/807a047255bf1e09303627c46dc043dca596b6958a354d904f32ab382005/greenlet-3.5.1-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:10a9a1c0bfbc93d41156ffcb90c75fbc05544054faf15dcc1fdf9765f8b607f0", size = 672962, upload-time = "2026-05-20T14:09:15.532Z" }, + { url = "https://files.pythonhosted.org/packages/96/27/5565b5b40389f1c7753003a07e21892fda8660926787036d5bc0308b8113/greenlet-3.5.1-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e630136e905fe5ff43e86945ae41220b6d1470956a39220e708110ac48d01ea5", size = 665697, upload-time = "2026-05-20T13:14:32.943Z" }, + { url = "https://files.pythonhosted.org/packages/76/32/19d4e13225193c29b13e308015223f7d75fd3d8623d49dd19040d2ce8ec1/greenlet-3.5.1-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:ef08c1567c78074b22d1a200183d52d04a14df447bf70bcbb6a3507a48e776fc", size = 476047, upload-time = "2026-05-20T14:01:44.39Z" }, + { url = "https://files.pythonhosted.org/packages/cf/82/e7de4178c0c2d1c9a5a3be3cc0b33e46a85b3ee4a77c071bf7ad8600e079/greenlet-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:975eac34b44a7077ca4d421348455b94f0f518246a7f14bc6d2fdcfe5b584368", size = 1621256, upload-time = "2026-05-20T14:02:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/00/10/f2dddcf7dacac17dfc68691809589adad06135eb28930429cf58a6467a2f/greenlet-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9ab3c3a0b2ae6198e67c898dad5215a49f9ae0d0081b3c3ec59f333e39eeca26", size = 1685956, upload-time = "2026-05-20T13:14:42.55Z" }, + { url = "https://files.pythonhosted.org/packages/22/17/4a232b32133230ada52f70e9d7f5b65b0caef8772f01849bd8d149e7e4ca/greenlet-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:cbfc69be86e10dcfef5b1e6269d1d6926552aa89ee39e1de3353360c1b6989ab", size = 239802, upload-time = "2026-05-20T13:13:15.481Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ae/4e623a7e6d4d2a5f4cb8e4c82de4169fc637942caae68d6e676b8a128ac5/greenlet-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:92fd6d44ac5e5a887c8a5dc4a8ba0ba908527c31c12f78c6bc7dcfe8aab279f6", size = 236853, upload-time = "2026-05-20T13:15:37.301Z" }, + { url = "https://files.pythonhosted.org/packages/7a/57/816d9cff29119da3505b3d6a5e14a8af89006ac36f47f891ff293ee05af1/greenlet-3.5.1-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:a6fdf2433a5441ef9a95464f7c3e674775da1c8c1177fff311cee1acad4626ed", size = 293877, upload-time = "2026-05-20T13:10:19.078Z" }, + { url = "https://files.pythonhosted.org/packages/23/a1/59b0a7c7d140ff1a75626680b9a9899b79a9176cab298b394968fb023295/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7546556f0d649f99f6a361098a55f761181bb2ea12ff150bb16d26092ad88244", size = 655333, upload-time = "2026-05-20T14:00:14.758Z" }, + { url = "https://files.pythonhosted.org/packages/72/1b/5efe127597625042218939d01855109f352779050768b670b52edcc16a6c/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d5ee3ea898009fa898f85f9982255d35278c477bebe185beca249cab42d4526c", size = 659443, upload-time = "2026-05-20T14:05:50.159Z" }, + { url = "https://files.pythonhosted.org/packages/c9/9d/1dcdf7b95ab3cf8c7b6d7277c18a5e167312f2b362ddfcc5d5e6d8d84b43/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a57b0d05a0448eed231d59c0ceb287dde984551e54cbc51ac2d4865712838e9c", size = 659998, upload-time = "2026-05-20T14:09:16.912Z" }, + { url = "https://files.pythonhosted.org/packages/6c/6d/c404246ea4d22d097a7426d0efb5b781bd7eb67715f09e79001bd552ab18/greenlet-3.5.1-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5c81f74d204d3edd136ebfd50dce53acbb776995d721a0fe801626cfc93b8cd", size = 658356, upload-time = "2026-05-20T13:14:35.091Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/c4959664fc231d587d66d8e81f2095e98056ba1954beafdcbe635e251052/greenlet-3.5.1-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:b0703c2cef53e01baec47f7a3868009913ad71ec678bbecb42a6f40895e4ce62", size = 494470, upload-time = "2026-05-20T14:01:45.611Z" }, + { url = "https://files.pythonhosted.org/packages/51/02/f8ee37fb6d2219329f350af241c27fcf12df57e723d11f6fc6d3bacdadaa/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:2c18ef16bf6d4dd410e4dd52996888ea1497be26892fe5bbc73580aba4287b8e", size = 1619216, upload-time = "2026-05-20T14:02:33.403Z" }, + { url = "https://files.pythonhosted.org/packages/93/c5/3dc9475ace2c7a3680da12372cddd7f1ac874eb410a1ac48d3e9dab83782/greenlet-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:17d86354f0ae6b61bf9be5148d0dd34e06c3cb7c602c671f79f29ac3b150e659", size = 1678427, upload-time = "2026-05-20T13:14:43.71Z" }, + { url = "https://files.pythonhosted.org/packages/df/4e/750c15c317a41ffb36f0bf40b933e3d744a7dede61889f74443ea69690cf/greenlet-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:e7516cf6ae6b8a582c2770a0caed47b8a48373ed732c33d69a72913ae6ac923e", size = 245225, upload-time = "2026-05-20T13:13:59.366Z" }, + { url = "https://files.pythonhosted.org/packages/4f/fd/d3baea2eeb7b617efd47e87ca06e2ec2c6118d303aa9e918e0ce16eadc10/greenlet-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:5028648bf2253ec4745add746129d3904121fa7fe871a76bed23c5720573ce0a", size = 239590, upload-time = "2026-05-20T13:13:37.382Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "identify" +version = "2.6.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/63/51723b5f116cc04b061cb6f5a561790abf249d25931d515cd375e063e0f4/identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842", size = 99567, upload-time = "2026-04-17T18:39:50.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", size = 99397, upload-time = "2026-04-17T18:39:49.221Z" }, +] + +[[package]] +name = "idna" +version = "3.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/88/bcf9709822fe69d02c2a6a77956c98ce6ea8ca8767a9aadcedc7eb6a2390/idna-3.16.tar.gz", hash = "sha256:d7a6da03db833450fca25d2358ac9ff06cd624577a4aea3a596d5c0f77b8e03d", size = 203770, upload-time = "2026-05-22T00:16:18.781Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "ipython" +version = "9.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "psutil" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274, upload-time = "2026-04-24T12:24:53.038Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + +[[package]] +name = "jedi" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416, upload-time = "2026-05-01T23:38:47.814Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812, upload-time = "2026-05-01T23:38:43.919Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "jsbeautifier" +version = "1.15.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "editorconfig" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ea/98/d6cadf4d5a1c03b2136837a435682418c29fdeb66be137128544cecc5b7a/jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592", size = 75257, upload-time = "2025-02-27T17:53:53.252Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/14/1c65fccf8413d5f5c6e8425f84675169654395098000d8bddc4e9d3390e1/jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528", size = 94707, upload-time = "2025-02-27T17:53:46.152Z" }, +] + +[[package]] +name = "json5" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/4b/6f8906aaf67d501e259b0adab4d312945bb7211e8b8d4dcc77c92320edaa/json5-0.14.0.tar.gz", hash = "sha256:b3f492fad9f6cdbced8b7d40b28b9b1c9701c5f561bef0d33b81c2ff433fefcb", size = 52656, upload-time = "2026-03-27T22:50:48.108Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/42/cf027b4ac873b076189d935b135397675dac80cb29acb13e1ab86ad6c631/json5-0.14.0-py3-none-any.whl", hash = "sha256:56cf861bab076b1178eb8c92e1311d273a9b9acea2ccc82c276abf839ebaef3a", size = 36271, upload-time = "2026-03-27T22:50:47.073Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, + { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, + { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, + { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, + { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, + { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, + { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, + { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, + { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, + { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, + { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, + { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, + { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, + { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, + { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, + { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" }, + { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" }, + { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" }, + { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" }, + { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" }, + { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" }, + { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" }, + { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" }, + { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" }, + { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" }, + { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" }, + { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" }, + { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" }, + { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" }, + { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" }, + { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" }, + { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, + { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, + { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, +] + +[[package]] +name = "librt" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" }, + { url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" }, + { url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" }, + { url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" }, + { url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" }, + { url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" }, + { url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" }, + { url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" }, + { url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" }, + { url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" }, + { url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" }, + { url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" }, + { url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" }, + { url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" }, + { url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/29/eb/dbce197da4e227779e56b5735f2decc3eb36e55a1cdbf1bd65d6639d76c1/librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd", size = 143345, upload-time = "2026-05-10T18:16:30.674Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/254bebd0c11c8ba684018efb8006ff22e466abce445215cca6c778e7d9de/librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4", size = 143131, upload-time = "2026-05-10T18:16:32.037Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3f/f77d6122d21ac7bf6ae8a7dfced1bd2a7ac545d3273ebdcaf8042f6d619f/librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8", size = 477024, upload-time = "2026-05-10T18:16:33.493Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0a/2c996dadebaa7d9bbbd43ef2d4f3e66b6da545f838a41694ef6172cebec8/librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b", size = 474221, upload-time = "2026-05-10T18:16:34.864Z" }, + { url = "https://files.pythonhosted.org/packages/0a/7e/f5d92af8486b8272c23b3e686b46ff72d89c8169585eb61eef01a2ac7147/librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175", size = 505174, upload-time = "2026-05-10T18:16:36.705Z" }, + { url = "https://files.pythonhosted.org/packages/af/1a/cb0734fe86398eb33193ab753b7326255c74cac5eb09e76b9b16536e7adb/librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03", size = 497216, upload-time = "2026-05-10T18:16:38.418Z" }, + { url = "https://files.pythonhosted.org/packages/18/06/094820f91558b66e29943c0ec41c9914f460f48dd51fc503c3101e10842d/librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c", size = 513921, upload-time = "2026-05-10T18:16:39.848Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c2/00de9018871a282f530cacb457d5ec0428f6ac7e6fedde9aff7468d9fb04/librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3", size = 520850, upload-time = "2026-05-10T18:16:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/51/9d/64631832348fd1834fb3a61b996434edddaaf25a31d03b0a76273159d2cf/librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96", size = 504237, upload-time = "2026-05-10T18:16:43.15Z" }, + { url = "https://files.pythonhosted.org/packages/a5/ec/ae5525eb16edc827a044e7bb8777a455ff95d4bca9379e7e6bddd7383647/librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe", size = 546261, upload-time = "2026-05-10T18:16:44.408Z" }, + { url = "https://files.pythonhosted.org/packages/5a/09/adce371f27ca039411da9659f7430fcc2ba6cd0c7b3e4467a0f091be7fa9/librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f", size = 96965, upload-time = "2026-05-10T18:16:46.039Z" }, + { url = "https://files.pythonhosted.org/packages/d6/ee/8ac720d98548f173c7ce2e632a7ca94673f74cacd5c8162a84af5b35958a/librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7", size = 115151, upload-time = "2026-05-10T18:16:47.133Z" }, + { url = "https://files.pythonhosted.org/packages/94/20/c900cf14efeb09b6bef2b2dff20779f73464b97fd58d1c6bccc379588ae3/librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1", size = 98850, upload-time = "2026-05-10T18:16:48.597Z" }, + { url = "https://files.pythonhosted.org/packages/0c/71/944bfe4b64e12abffcd3c15e1cce07f72f3d55655083786285f4dedeb532/librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72", size = 151138, upload-time = "2026-05-10T18:16:49.839Z" }, + { url = "https://files.pythonhosted.org/packages/b6/10/99e64a5c86989357fda078c8143c533389585f6473b7439172dd8f3b3b2d/librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa", size = 151976, upload-time = "2026-05-10T18:16:51.062Z" }, + { url = "https://files.pythonhosted.org/packages/21/31/5072ad880946d83e5ea4147d6d018c78eefce85b77819b19bdd0ee229435/librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548", size = 557927, upload-time = "2026-05-10T18:16:52.632Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8d/70b5fb7cfbab60edbe7381614ab985da58e144fbf465c86d44c95f43cdca/librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2", size = 539698, upload-time = "2026-05-10T18:16:53.934Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a3/ba3495a0b3edbd24a4cae0d1d3c64f39a9fc45d06e812101289b50c1a619/librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f", size = 577162, upload-time = "2026-05-10T18:16:55.589Z" }, + { url = "https://files.pythonhosted.org/packages/f7/db/36e25fb81f99937ff1b96612a1dc9fd66f039cb9cc3aee12c01fac31aab9/librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51", size = 566494, upload-time = "2026-05-10T18:16:56.975Z" }, + { url = "https://files.pythonhosted.org/packages/33/0d/3f622b47f0b013eeb9cf4cc07ae9bfe378d832a4eec998b2b209fe84244d/librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2", size = 596858, upload-time = "2026-05-10T18:16:58.374Z" }, + { url = "https://files.pythonhosted.org/packages/a9/02/71b90bc93039c46a2000651f6ad60122b114c8f54c4ad306e0e96f5b75ad/librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085", size = 590318, upload-time = "2026-05-10T18:16:59.676Z" }, + { url = "https://files.pythonhosted.org/packages/04/04/418cb3f75621e2b761fb1ab0f017f4d70a1a72a6e7c74ee4f7e8d198c2f3/librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3", size = 575115, upload-time = "2026-05-10T18:17:01.007Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2c/5a2183ac58dd911f26b5d7e7d7d8f1d87fcecdddd99d6c12169a258ff62c/librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd", size = 617918, upload-time = "2026-05-10T18:17:02.682Z" }, + { url = "https://files.pythonhosted.org/packages/15/1f/dc6771a52592a4451be6effa200cbfc9cec61e4393d3033d81a9d307961d/librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8", size = 103562, upload-time = "2026-05-10T18:17:03.99Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/7d1415567027286a75ba1093ec4aca11f073e0f559c530cf3e0a757ad55c/librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c", size = 124327, upload-time = "2026-05-10T18:17:05.465Z" }, + { url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.10.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908, upload-time = "2026-04-24T00:12:21.323Z" }, + { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016, upload-time = "2026-04-24T00:12:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336, upload-time = "2026-04-24T00:12:26.096Z" }, + { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602, upload-time = "2026-04-24T00:12:29.052Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966, upload-time = "2026-04-24T00:12:32.131Z" }, + { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462, upload-time = "2026-04-24T00:12:35.226Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688, upload-time = "2026-04-24T00:12:37.442Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331, upload-time = "2026-04-24T00:12:39.688Z" }, + { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461, upload-time = "2026-04-24T00:12:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091, upload-time = "2026-04-24T00:12:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027, upload-time = "2026-04-24T00:12:47.583Z" }, + { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269, upload-time = "2026-04-24T00:12:50.878Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588, upload-time = "2026-04-24T00:12:53.784Z" }, + { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913, upload-time = "2026-04-24T00:12:56.501Z" }, + { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019, upload-time = "2026-04-24T00:12:58.896Z" }, + { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645, upload-time = "2026-04-24T00:13:01.406Z" }, + { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194, upload-time = "2026-04-24T00:13:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684, upload-time = "2026-04-24T00:13:06.647Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790, upload-time = "2026-04-24T00:13:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571, upload-time = "2026-04-24T00:13:13.087Z" }, + { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292, upload-time = "2026-04-24T00:13:15.546Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276, upload-time = "2026-04-24T00:13:18.318Z" }, + { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218, upload-time = "2026-04-24T00:13:20.974Z" }, + { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145, upload-time = "2026-04-24T00:13:23.228Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085, upload-time = "2026-04-24T00:13:25.849Z" }, + { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358, upload-time = "2026-04-24T00:13:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970, upload-time = "2026-04-24T00:13:31.904Z" }, + { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785, upload-time = "2026-04-24T00:13:34.511Z" }, + { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999, upload-time = "2026-04-24T00:13:36.962Z" }, + { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543, upload-time = "2026-04-24T00:13:39.851Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800, upload-time = "2026-04-24T00:13:42.296Z" }, + { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561, upload-time = "2026-04-24T00:13:45.026Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884, upload-time = "2026-04-24T00:13:48.066Z" }, + { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333, upload-time = "2026-04-24T00:13:51.008Z" }, + { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785, upload-time = "2026-04-24T00:13:53.633Z" }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150, upload-time = "2026-05-08T17:33:33.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534, upload-time = "2026-05-08T17:33:32.055Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "meridian-euid" +version = "0.4.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/ae/2e51a25fd422f39d0b8cc7b65ad9f32309efad6cadaaa15ab16f29585a98/meridian_euid-0.4.5.tar.gz", hash = "sha256:5423d40c4a200d86dc1e0b5df47d3d1aecdf5a30f9668849e4f857deb956e218", size = 15247, upload-time = "2026-05-16T11:47:36.548Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/8d/02dc20a37f126beca0ca197796be8ee44db463c9417efa350715046915d0/meridian_euid-0.4.5-py3-none-any.whl", hash = "sha256:c07d760f26d47a7cee489b71fe11a4df69a9ba094cde57aad2fca8415a05bee8", size = 12073, upload-time = "2026-05-16T11:47:35.645Z" }, +] + +[[package]] +name = "moto" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "boto3" }, + { name = "botocore" }, + { name = "cryptography" }, + { name = "requests" }, + { name = "responses" }, + { name = "werkzeug" }, + { name = "xmltodict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/e9/c38202162db2e76623176be9f1dbc9aa41228ffa91ee8da2d3986082c3e3/moto-5.2.1.tar.gz", hash = "sha256:ccb2f3e1dfa82e50e054bda98b0be708d244d2668364dcc1d45e8d3de6091bde", size = 8634437, upload-time = "2026-05-10T19:11:57.286Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/79/8085b7c1ecd48d0535c3c8444a1d8df2926e457dce8e55fabc332a382c9c/moto-5.2.1-py3-none-any.whl", hash = "sha256:19d2fbd6e613aa5b4e364c52cd5d3cea371643a0f4210689a703227bd2924c5c", size = 6671379, upload-time = "2026-05-10T19:11:53.543Z" }, +] + +[[package]] +name = "mypy" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ast-serialize" }, + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" }, + { url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" }, + { url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" }, + { url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" }, + { url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" }, + { url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" }, + { url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ca/b279a672e874aedd5498ae25f722dacc8aa86bbffb939b3f97cbb1cf6686/mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2", size = 14848422, upload-time = "2026-05-11T18:35:45.984Z" }, + { url = "https://files.pythonhosted.org/packages/27/e6/3efe56c631d959b9b4454e208b0ac4b7f4f58b404c89f8bec7b49efdfc21/mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f", size = 13677374, upload-time = "2026-05-11T18:36:57.188Z" }, + { url = "https://files.pythonhosted.org/packages/84/7f/8107ea87a44fd1f1b59882442f033c9c3488c127201b1d1d15f1cbd6022e/mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4", size = 14055743, upload-time = "2026-05-11T18:35:18.361Z" }, + { url = "https://files.pythonhosted.org/packages/51/4d/b6d34db183133b83761b9199a82d31557cdbb70a380d8c3b3438e11882a3/mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef", size = 15020937, upload-time = "2026-05-11T18:34:59.618Z" }, + { url = "https://files.pythonhosted.org/packages/ff/d7/f08360c691d758acb02f45022c34d98b92892f4ea756644e1000d4b9f3d8/mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135", size = 15253371, upload-time = "2026-05-11T18:36:41.081Z" }, + { url = "https://files.pythonhosted.org/packages/67/1b/09460a13719530a19bce27bd3bc8449e83569dd2ba7faf51c9c3c30c0b61/mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21", size = 11326429, upload-time = "2026-05-11T18:34:13.526Z" }, + { url = "https://files.pythonhosted.org/packages/40/62/75dbf0f82f7b6680340efc614af29dd0b3c17b8a4f1cd09b8bd2fd6bc814/mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57", size = 10218799, upload-time = "2026-05-11T18:32:23.491Z" }, + { url = "https://files.pythonhosted.org/packages/b2/66/caca04ed7d972fb6eb6dd1ccd6df1de5c38fae8c5b3dc1c4e8e0d85ee6b9/mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e", size = 15923458, upload-time = "2026-05-11T18:35:28.64Z" }, + { url = "https://files.pythonhosted.org/packages/ed/52/2d90cbe49d014b13ed7ff337930c30bad35893fe38a1e4641e756bb62191/mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780", size = 14757697, upload-time = "2026-05-11T18:36:14.208Z" }, + { url = "https://files.pythonhosted.org/packages/ac/37/d98f4a14e081b238992d0ed96b6d39c7cc0148c9699eb71eaa68629665ea/mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd", size = 15405638, upload-time = "2026-05-11T18:33:48.249Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c2/15c46613b24a84fad2aea1248bf9619b99c2767ae9071fe224c179a0b7d4/mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08", size = 16215852, upload-time = "2026-05-11T18:32:50.296Z" }, + { url = "https://files.pythonhosted.org/packages/5c/90/9c16a57f482c76d25f6379762b56bbf65c711d8158cf271fb2802cfb0640/mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081", size = 16452695, upload-time = "2026-05-11T18:33:38.182Z" }, + { url = "https://files.pythonhosted.org/packages/0f/4c/215a4eeb63cacc5f17f516691ea7285d11e249802b942476bff15922a314/mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7", size = 12866622, upload-time = "2026-05-11T18:34:39.945Z" }, + { url = "https://files.pythonhosted.org/packages/4b/50/1043e1db5f455ffe4c9ab22747cd8ca2bc492b1e4f4e21b130a44ee2b217/mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6", size = 10610798, upload-time = "2026-05-11T18:36:31.444Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/f1/392f8c5bfc16f66a0d2d41561c01627c228fe7ed2a0d056ef11315042570/pandas-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fed2ff7fd9779120e388e285fc029bd5cf9490cdd2e4166a9ee22c0e49a9ab09", size = 10357846, upload-time = "2026-05-11T18:52:36.143Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3d/b16412745651e855f357e5e66930248688378853a6e2698a214e331fba1f/pandas-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b168fc218fd80a6cbdbdbc1a97ddc7889ed057d7eb45f50d866ceab5f39904c4", size = 9899550, upload-time = "2026-05-11T18:52:38.976Z" }, + { url = "https://files.pythonhosted.org/packages/31/a8/fa2535168fffcedf67f4f6de28d2dd903a747ca7c8ea6989451aaeb3a92f/pandas-3.0.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0383c72c75cdcca61a9e116e611143902dbfd08bff356829c2f6d1cf40a9ca8c", size = 10412965, upload-time = "2026-05-11T18:52:41.915Z" }, + { url = "https://files.pythonhosted.org/packages/65/b6/09b01cdbc15224e2850365192d17b7bdebb8bdbd8780ed221fcdf0d9a515/pandas-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6dc0b3fd2169c9157deed50b4d519553a3655c8c6a96027136d654592be973a9", size = 10894600, upload-time = "2026-05-11T18:52:45.02Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a4/2eb28f2fccb4ced4a2c79ab2a5dee9ade1ebf44922ebad6fea158c9f95d4/pandas-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e65d5407dc0b394f509699650e4a2ec01c0514f21850f453fa60f3be79a5dbf", size = 11422824, upload-time = "2026-05-11T18:52:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/f8/45/830bb57f533a4604b355e07edcb8ea18cf88b5f94e5fca92f27052d7c597/pandas-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8894dc474d648fe7b6ff0ca9b0bd73950d19952bc1a6534540762c5d79d305c", size = 11950889, upload-time = "2026-05-11T18:52:50.905Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c5/fc1b368f303087d20e8c9bf3d6ceb186263cfac0ade735cd938538bea839/pandas-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c7be265b62cef88e253a941e4698604973736dcfe242fdb5198f0f7bc473cdcc", size = 9755463, upload-time = "2026-05-11T18:52:53.386Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/fda8f9705b1b09c6ebe14bfc0fa0e4ec8584d54ea673628f157ff55131af/pandas-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:557409bc4178e70ee8d9ddb494798e51ebf6ea59330f6be22c51bab2a7db6c49", size = 9066158, upload-time = "2026-05-11T18:52:56.038Z" }, + { url = "https://files.pythonhosted.org/packages/c5/90/62d8302883c44308c477e222c3daf7c813a34c8e96985882fbd53d964352/pandas-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:67b3b64c11910cfa29f4e94a14d3bff9ee693b6fc76055e7cad549cee0aec5fa", size = 10331071, upload-time = "2026-05-11T18:52:58.838Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ae/6a6493c783a101f165e4356953ba3c74d6f77f0042fa7d753da9dfbb640c/pandas-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39436b377d56d2a2e52d0395bdbee171f01068e99af5250509aceeb929f765c7", size = 9875690, upload-time = "2026-05-11T18:53:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/62/7c/5df8e9f56c69a2769fbe9382a5ef8f2658c007e376434e1e2cbb57ad895f/pandas-3.0.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4be06d68f9ddcfc645b87534911da79a8fbffc7573c80e0edcf42a5020624d8", size = 10381634, upload-time = "2026-05-11T18:53:04.393Z" }, + { url = "https://files.pythonhosted.org/packages/99/68/1237369725aa617bb358263d535803e3053fdbc593513ec5ed9c9896b5b6/pandas-3.0.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a4eeb6830daf35a71cc09649bd823e2b542dac246cdee9614c6e4bd65028cd6a", size = 10891243, upload-time = "2026-05-11T18:53:07.643Z" }, + { url = "https://files.pythonhosted.org/packages/25/93/77d108e8af7222b4a503ebde0e30215b1c2e4f8e53a526431890f22d5586/pandas-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1928e07221f82db493cd4af1e23c1bfca524a19a4699887975bff68f49a72bfb", size = 11388659, upload-time = "2026-05-11T18:53:10.634Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bd/eff5b4399f332ac386c853f6cd2bd3fa2ca0061b9f36ecd9c4d7c4265649/pandas-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51b1fe551acb77dac643c6fda86084d8d446c10fe64b06a9cc29c4cc8540e7f2", size = 11942880, upload-time = "2026-05-11T18:53:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/2c/20/559ace4200982c3887d0b86bfd0d856a2143ef8ddab63cc07934951a964c/pandas-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:a82d532a3351d435432cd913edbccaf8b8e01d4dd0e5ced5a8d2e8ecd94c7e44", size = 9757091, upload-time = "2026-05-11T18:53:16.306Z" }, + { url = "https://files.pythonhosted.org/packages/3a/66/69055a09fe200f29f922a3eeec4804611900b95f52d932ece3393c3c0c19/pandas-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:275c14e0fce14a2ec20eee474aecd305478ea3c1e6f6a9d8fe219a165542717e", size = 9057282, upload-time = "2026-05-11T18:53:18.768Z" }, + { url = "https://files.pythonhosted.org/packages/57/0e/efe801b0e6811e8e650cd21b7f2608e30f08a7067e2bf6e8752b0d56ee3c/pandas-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:46997386d528eb40376ecd6b033cf4a8a1e5282580f68f43de875b78cba2199d", size = 10767016, upload-time = "2026-05-11T18:53:21.227Z" }, + { url = "https://files.pythonhosted.org/packages/ea/dc/eb55135a1d5f0f0519f28da1f609a206d2cad1f9c35c32d51e38dd7261ae/pandas-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261e308dfb22448384b7580cf719d2f998fe2966c92893c3e77d14008af1f066", size = 10420210, upload-time = "2026-05-11T18:53:23.982Z" }, + { url = "https://files.pythonhosted.org/packages/c6/3e/b1d5d955ce33ffecb407465a60bc32769d74fcf68224b7ae67ae11d4dea4/pandas-3.0.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dd1a5d1def6a46002e964510bdc67c368aa0951df5d1d9f8365336f5a1f490cd", size = 10336126, upload-time = "2026-05-11T18:53:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/a01261711ab60a22d71b862f0de20e4c504bf80457270ad8cb42110f6abc/pandas-3.0.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d72828c20c6d6e83e1e22a6a3b47b326b71664112fa9705dcbccfd7a39b62085", size = 10728051, upload-time = "2026-05-11T18:53:29.125Z" }, + { url = "https://files.pythonhosted.org/packages/e9/21/ea191195e587b18cf682e97f433f81b2d0fbe341380e80a3e0d6e4403c8e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d26cbe1fcfc12e8fd900e2454163e466b2d3af84f7c75481df7683ffc073d870", size = 11350796, upload-time = "2026-05-11T18:53:32.056Z" }, + { url = "https://files.pythonhosted.org/packages/64/69/f0eaaf54939f0e8c6768fd06be9af2cef9b36048b96dfb9e1b2c685a807e/pandas-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3e91cec1879ada0624fc3dc9953c5cbd60208e59c0db28f540c5d6d47502422f", size = 11799741, upload-time = "2026-05-11T18:53:34.985Z" }, + { url = "https://files.pythonhosted.org/packages/45/a4/865e0e510cae5fc2194de4db28be638952de942571ba9125934fd9c01d47/pandas-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:08d789b41f87e0905880e293cedf6197ce71fe67cc081358b1e148a491b9bd13", size = 10499958, upload-time = "2026-05-11T18:53:37.857Z" }, + { url = "https://files.pythonhosted.org/packages/86/54/effdcc3c0ff7a08037889200e148ebe94c16c4f653be078c7b3675955df1/pandas-3.0.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3650109c0f22879df8bd6179ab9ee3d7f1d1d4e7e0094a3f0032d9f51e2e64ac", size = 10336065, upload-time = "2026-05-11T18:53:41.099Z" }, + { url = "https://files.pythonhosted.org/packages/68/10/bf2d6738d72748b961a3751ab89522d58c54efc36a8e1a12161216cd45cf/pandas-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bab900348131a7db1f69a7309ef141fd5680f1487094193bcbbb61791573bf8f", size = 9926101, upload-time = "2026-05-11T18:53:43.515Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e9/e35cf11c8a136e757b956f5f0efdcaa50aecde85ea055f1898dfc68262f3/pandas-3.0.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba7e08b9ac1d54569cd1e256e3668975ed624d6826f7b68df0342b012007bddb", size = 10457553, upload-time = "2026-05-11T18:53:46.394Z" }, + { url = "https://files.pythonhosted.org/packages/58/3b/1cdec6772bdbaf7b25dab360c59f03cadf05492dd724c6540af905389b07/pandas-3.0.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d71c63ae4ebdbf70209742096f1fc46a83a0613c99d4b23766cced9ff8cd62a", size = 10914065, upload-time = "2026-05-11T18:53:49.134Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/1ef644445fcd72e3627bceec77e3560636f87ddce4ed841afe76b83b5bf9/pandas-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e3a2ec42c98ffa2565a67e08e218d06d72576d758d90facb7c00805194d8f360", size = 11459188, upload-time = "2026-05-11T18:53:52.527Z" }, + { url = "https://files.pythonhosted.org/packages/7e/49/4d8d4f42cbc9c4adc7a1870f269c02cbd6cd40d059622c06fb298addcbad/pandas-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:335f62418ed562cfc3c49e9e196375c28b729dcef8543abf4f9438e381bf3c76", size = 11982966, upload-time = "2026-05-11T18:53:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/38/55/792619469bab9882d8bbd5865d45a72f6478762d04a9af4bf0d08c503e95/pandas-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:3c20a521bbb85902f79f7270c80a59e1b5452d96d170c034f207181870f97ac5", size = 9876755, upload-time = "2026-05-11T18:53:58.067Z" }, + { url = "https://files.pythonhosted.org/packages/2a/af/33c469653b0ba03b50c3a98192d4c07f0c75c66b263ceb097fce0ee97d31/pandas-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:a2d2dff8a04f3917b55ab3910c32990f8ddf7eceba114947838cefa976a68977", size = 9198658, upload-time = "2026-05-11T18:54:00.733Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fa/b8c257bd76b8bd060c3a9151c1fca05e9b9c5e3af5d0f549c0356f6d143d/pandas-3.0.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:0d589105b3c14645af1738ff279b2995102d8f7a03b0a66dc8d95550eb513e04", size = 10787242, upload-time = "2026-05-11T18:54:03.564Z" }, + { url = "https://files.pythonhosted.org/packages/54/eb/f19206ffb0bf1919002969aa448b4702c6594845156a6f8050674855aac3/pandas-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:13fc1e853d9e04743d11ba75a985ccbc2a317fe07d8af61e445a6fd24dacd6a6", size = 10436369, upload-time = "2026-05-11T18:54:06.311Z" }, + { url = "https://files.pythonhosted.org/packages/fd/24/c7c39fb4fe22b71a0c2d78bf0c585c600092d85f94f086d2b3b2f6ca27e2/pandas-3.0.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:819959dab7bbd0049c15623fbac4e29a191b9528160a61fb1032242d8ced2d9c", size = 10358306, upload-time = "2026-05-11T18:54:09.085Z" }, + { url = "https://files.pythonhosted.org/packages/16/ec/dd2a9eb7fa1204df88c0864164e35b228ac581062ac612ba0a67fd812e4c/pandas-3.0.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:60ae316d3fd75d1858d450d0db0103ea2be3e7d4a95ec2f064f7e2ae63f7b028", size = 10758394, upload-time = "2026-05-11T18:54:11.956Z" }, + { url = "https://files.pythonhosted.org/packages/95/6e/00c61ea8e85b4f6d8d35e11852a1a4998fc7fafc91c6a602d1cc9c972d64/pandas-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd3a518890b400d32f9023722dc9a9a5c969f00b415419a3c06c043f09bb5d7d", size = 11375717, upload-time = "2026-05-11T18:54:14.539Z" }, + { url = "https://files.pythonhosted.org/packages/31/89/8fc1c268969fac43688d65fd92e67df24bd128d53cb4d2eee534cd307399/pandas-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c39be2d709d01fa972a0cabc522389fceca4f3969332ba25a7d6c5802cf976a", size = 11828897, upload-time = "2026-05-11T18:54:17.146Z" }, + { url = "https://files.pythonhosted.org/packages/56/3b/e7d20dea247a3e6dc0bd8a6953854afbedc03951def4e7371e05e7263e25/pandas-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4db8c527972a821cf5286b40ccc57642a39bc62e62022b42f99f8a67fca8c3a1", size = 10900855, upload-time = "2026-05-11T18:54:19.72Z" }, + { url = "https://files.pythonhosted.org/packages/0f/54/68a0978d1ef8502b8492099beaa6e7a0c1b32e3b5d4f677f5810cb08711c/pandas-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b2c95f8bfc1ee412bf482605d7bfd30c12d1d26bd59fdd91efeef1d4718decb1", size = 9466464, upload-time = "2026-05-11T18:54:22.754Z" }, +] + +[[package]] +name = "parso" +version = "0.8.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824, upload-time = "2026-05-01T23:13:02.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025, upload-time = "2026-05-01T23:12:58.867Z" }, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, +] + +[[package]] +name = "playwright" +version = "1.60.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet" }, + { name = "pyee" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/f0/832bd9677194908da118064eef20082f2791e3d18215cc6d9391ee2c5a67/playwright-1.60.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:6a8cd0fec171fb3089e95e898c8bc8a6f35dea0b78b399e12fcc19427e91b1d7", size = 43474635, upload-time = "2026-05-18T12:00:31.969Z" }, + { url = "https://files.pythonhosted.org/packages/59/7b/e1d32ae8a3ed937ec2be3721c5f728b13d731a0b7c6442e0b3bec5094ac0/playwright-1.60.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:39b5420ba6145045b69ced4c5c47d4d9fe5bddfc8ff816c518913afcb25ec7a5", size = 42261327, upload-time = "2026-05-18T12:00:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/d7/bc/23de499ded6411c188a20c5a0dea6f0cd4ed5d2b3cc6042a5dbd3ed609aa/playwright-1.60.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:2581d0e6a3392c71f91b27460c7fd093356818dc430f48153896c8aeeaef7705", size = 43474636, upload-time = "2026-05-18T12:00:39.294Z" }, + { url = "https://files.pythonhosted.org/packages/22/7b/1d679f4fced4ea94efadd17103856d8c565384f68382a1681264e46f5925/playwright-1.60.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:1c2bfae7884fb3fb05b853290eab8f343d524e5016f2f1def702acbbdf14c93e", size = 47467220, upload-time = "2026-05-18T12:00:43.179Z" }, + { url = "https://files.pythonhosted.org/packages/84/c2/1528d267d4442bd2c6b8eaeab819dd52c2030bf80e89293f0ba1f687473b/playwright-1.60.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43e66564125ee31b07a58cefb21e256d62d67d8d1713e6858df7a3019d8ed353", size = 47154856, upload-time = "2026-05-18T12:00:46.715Z" }, + { url = "https://files.pythonhosted.org/packages/bb/4e/b008b6440a7a1624378041da94829956d4b8f7ab9ef5aad22d0dc3f2e26d/playwright-1.60.0-py3-none-win32.whl", hash = "sha256:ec94e416ea320711e0ad4bf185dcbf41833672961e90773e1885255d7db7b7e7", size = 37902157, upload-time = "2026-05-18T12:00:50.374Z" }, + { url = "https://files.pythonhosted.org/packages/55/f0/0541524133104f9cc20bf900870ff4a736b76a23483f3a55295ddfa58409/playwright-1.60.0-py3-none-win_amd64.whl", hash = "sha256:9566821ce6030a1f9e7146a24e19355ab0d98805fd0f9be50bb3d8fef1750c02", size = 37902159, upload-time = "2026-05-18T12:00:53.728Z" }, + { url = "https://files.pythonhosted.org/packages/80/c8/210f282d278e4709cdd71b12a31af45a30a22ab3207b387e29b37e478713/playwright-1.60.0-py3-none-win_arm64.whl", hash = "sha256:6e4f6700a4c2250efff8e690a81d66e3855754fb587b6b87cf5c784014f91537", size = 34037981, upload-time = "2026-05-18T12:00:57.584Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/22/2de9408ac81acbb8a7d05d4cc064a152ccf33b3d480ebe0cd292153db239/pre_commit-4.6.0.tar.gz", hash = "sha256:718d2208cef53fdc38206e40524a6d4d9576d103eb16f0fec11c875e7716e9d9", size = 198525, upload-time = "2026-04-21T20:31:41.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "psycopg2" +version = "2.9.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/5e/dc6acaf46d78979d6b03458b7a1618a68e152a6776fce95daac5e0f0301b/psycopg2-2.9.9.tar.gz", hash = "sha256:d1454bde93fb1e224166811694d600e746430c006fbb031ea06ecc2ea41bf156", size = 384926, upload-time = "2023-10-03T12:48:53.323Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/13/f74ffe6b6f58822e807c70391dc5679a53feb92ce119ccb8a6546c3fb893/psycopg2-2.9.9-cp312-cp312-win32.whl", hash = "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024", size = 1024634, upload-time = "2023-10-28T09:37:19.078Z" }, + { url = "https://files.pythonhosted.org/packages/58/4b/c4a26e191882b60150bfcb639e416524ae7f8249ab7ee854fb5247f16c40/psycopg2-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693", size = 1163789, upload-time = "2023-10-28T09:37:22.214Z" }, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/60/a3624f79acea344c16fbef3a94d28b89a8042ddfb8f3e4ca83f538671409/psycopg2_binary-2.9.12.tar.gz", hash = "sha256:5ac9444edc768c02a6b6a591f070b8aae28ff3a99be57560ac996001580f294c", size = 379686, upload-time = "2026-04-21T09:40:34.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/9f/ef4ef3c8e15083df90ca35265cfd1a081a2f0cc07bb229c6314c6af817f4/psycopg2_binary-2.9.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5cdc05117180c5fa9c40eea8ea559ce64d73824c39d928b7da9fb5f6a9392433", size = 3712459, upload-time = "2026-04-20T23:34:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/b5/01/3dd14e46ba48c1e1a6ec58ee599fa1b5efa00c246d5046cd903d0eeb1af1/psycopg2_binary-2.9.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3227a3bc228c10d21011a99245edca923e4e8bf461857e869a507d9a41fe9f6", size = 3822936, upload-time = "2026-04-20T23:34:32.77Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f7/0640e4901119d8a9f7a1784b927f494e2198e213ceb593753d1f2c8b1b30/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:995ce929eede89db6254b50827e2b7fd61e50d11f0b116b29fffe4a2e53c4580", size = 4578676, upload-time = "2026-04-20T23:34:35.18Z" }, + { url = "https://files.pythonhosted.org/packages/b0/55/44df3965b5f297c50cc0b1b594a31c67d6127a9d133045b8a66611b14dfb/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9fe06d93e72f1c048e731a2e3e7854a5bfaa58fc736068df90b352cefe66f03f", size = 4274917, upload-time = "2026-04-20T23:34:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4b/74535248b1eac0c9336862e8617c765ac94dac76f9e25d7c4a79588c8907/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40e7b28b63aaf737cb3a1edc3a9bbc9a9f4ad3dcb7152e8c1130e4050eddcb7d", size = 5894843, upload-time = "2026-04-20T23:34:40.856Z" }, + { url = "https://files.pythonhosted.org/packages/f2/ba/f1bf8d2ae71868ad800b661099086ee52bc0f8d9f05be1acd8ebb06757cc/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:89d19a9f7899e8eb0656a2b3a08e0da04c720a06db6e0033eab5928aabe60fa9", size = 4110556, upload-time = "2026-04-20T23:34:44.016Z" }, + { url = "https://files.pythonhosted.org/packages/45/46/c15706c338403b7c420bcc0c2905aad116cc064545686d8bf85f1999ea00/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:612b965daee295ae2da8f8218ce1d274645dc76ef3f1abf6a0a94fd57eff876d", size = 3655714, upload-time = "2026-04-20T23:34:46.233Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7c/a2d5dc09b64a4564db242a0fe418fde7d33f6f8259dd2c5b9d7def00fb5a/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b9a339b79d37c1b45f3235265f07cdeb0cb5ad7acd2ac7720a5920989c17c24e", size = 3301154, upload-time = "2026-04-20T23:34:49.528Z" }, + { url = "https://files.pythonhosted.org/packages/c0/e8/cc8c9a4ce71461f9ec548d38cadc41dc184b34c73e6455450775a9334ccd/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3471336e1acfd9c7fe507b8bad5af9317b6a89294f9eb37bd9a030bb7bebcdc6", size = 3048882, upload-time = "2026-04-20T23:34:51.86Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/31e2296bc0787c5ab75d3d118e40b239db8151b5192b90b77c72bc9256e9/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7af18183109e23502c8b2ae7f6926c0882766f35b5175a4cd737ad825e4d7a1b", size = 3351298, upload-time = "2026-04-20T23:34:54.124Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a8/75f4e3e11203b590150abed2cf7794b9c9c9f7eceddae955191138b44dde/psycopg2_binary-2.9.12-cp312-cp312-win_amd64.whl", hash = "sha256:398fcd4db988c7d7d3713e2b8e18939776fd3fb447052daae4f24fa39daede4c", size = 2757230, upload-time = "2026-04-20T23:34:56.242Z" }, + { url = "https://files.pythonhosted.org/packages/91/bb/4608c96f970f6e0c56572e87027ef4404f709382a3503e9934526d7ba051/psycopg2_binary-2.9.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7c729a73c7b1b84de3582f73cdd27d905121dc2c531f3d9a3c32a3011033b965", size = 3712419, upload-time = "2026-04-20T23:34:58.754Z" }, + { url = "https://files.pythonhosted.org/packages/5e/af/48f76af9d50d61cf390f8cd657b503168b089e2e9298e48465d029fcc713/psycopg2_binary-2.9.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4413d0caef93c5cf50b96863df4c2efe8c269bf2267df353225595e7e15e8df7", size = 3822990, upload-time = "2026-04-20T23:35:00.821Z" }, + { url = "https://files.pythonhosted.org/packages/7a/df/aba0f99397cd811d32e06fc0cc781f1f3ce98bc0e729cb423925085d781a/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4dfcf8e45ebb0c663be34a3442f65e17311f3367089cd4e5e3a3e8e62c978777", size = 4578696, upload-time = "2026-04-20T23:35:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/eaa74021ac4e4d5c2f83d82fc6615a63f4fe6c94dc4e94c3990427053f67/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c41321a14dd74aceb6a9a643b9253a334521babfa763fa873e33d89cfa122fb5", size = 4274982, upload-time = "2026-04-20T23:35:05.583Z" }, + { url = "https://files.pythonhosted.org/packages/35/ed/c25deff98bd26187ba48b3b250a3ffc3037c46c5b89362534a15d200e0db/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83946ba43979ebfdc99a3cd0ee775c89f221df026984ba19d46133d8d75d3cd9", size = 5894867, upload-time = "2026-04-20T23:35:07.902Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/8d0e21ca77373c6c9589e5c4528f6e8f0c08c62cafc76fb0bddb7a2cee22/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:411e85815652d13560fbe731878daa5d92378c4995a22302071890ec3397d019", size = 4110578, upload-time = "2026-04-20T23:35:10.149Z" }, + { url = "https://files.pythonhosted.org/packages/00/fc/f481e2435bd8f742d0123309174aae4165160ad3ef17c1b99c3622c241d2/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c8ad4c08e00f7679559eaed7aff1edfffc60c086b976f93972f686384a95e2c", size = 3655816, upload-time = "2026-04-20T23:35:12.56Z" }, + { url = "https://files.pythonhosted.org/packages/53/79/b9f46466bdbe9f239c96cde8be33c1aace4842f06013b47b730dc9759187/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:00814e40fa23c2b37ef0a1e3c749d89982c73a9cb5046137f0752a22d432e82f", size = 3301307, upload-time = "2026-04-20T23:35:15.029Z" }, + { url = "https://files.pythonhosted.org/packages/3f/19/7dc003b32fe35024df89b658104f7c8538a8b2dcbde7a4e746ce929742e7/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:98062447aebc20ed20add1f547a364fd0ef8933640d5372ff1873f8deb9b61be", size = 3048968, upload-time = "2026-04-20T23:35:16.757Z" }, + { url = "https://files.pythonhosted.org/packages/91/58/2dbd7db5c604d45f4950d988506aae672a14126ec22998ced5021cbb76bb/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:66a7685d7e548f10fb4ce32fb01a7b7f4aa702134de92a292c7bd9e0d3dbd290", size = 3351369, upload-time = "2026-04-20T23:35:18.933Z" }, + { url = "https://files.pythonhosted.org/packages/42/ee/dee8dcaad07f735824de3d6563bc67119fa6c28257b17977a8d624f02fab/psycopg2_binary-2.9.12-cp313-cp313-win_amd64.whl", hash = "sha256:b6937f5fe4e180aeee87de907a2fa982ded6f7f15d7218f78a083e4e1d68f2a0", size = 2757347, upload-time = "2026-04-20T23:35:21.283Z" }, + { url = "https://files.pythonhosted.org/packages/13/1b/708c0dca874acfad6d65314271859899a79007686f3a1f74e82a2ed4b645/psycopg2_binary-2.9.12-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f3b3de8a74ef8db215f22edffb19e32dc6fa41340456de7ec99efdc8a7b3ec2", size = 3712428, upload-time = "2026-04-20T23:35:23.453Z" }, + { url = "https://files.pythonhosted.org/packages/d6/39/ddbea9d4b4de6aca9431b6ed253f530f8a02d3b8f9bcfd0dbfe2b3de6fe4/psycopg2_binary-2.9.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1006fb62f0f0bc5ce256a832356c6262e91be43f5e4eb15b5eaf38079464caf2", size = 3823184, upload-time = "2026-04-20T23:35:25.92Z" }, + { url = "https://files.pythonhosted.org/packages/bf/a0/bc2fef74b106fa345567122a0659e6d94512ed7dc0131ec44c9e5aba3725/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:840066105706cd2eb29b9a1c2329620056582a4bf3e8169dec5c447042d0869f", size = 4579157, upload-time = "2026-04-20T23:35:28.542Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/d4e3b2005d3de607ca4fbb0e8742e248056e52184a6b94ebda3c1c2c329b/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:863f5d12241ebe1c76a72a04c2113b6dc905f90b9cef0e9be0efd994affd9354", size = 4274970, upload-time = "2026-04-20T23:35:30.418Z" }, + { url = "https://files.pythonhosted.org/packages/2e/42/c9853f8db3967fe08bcde11f53d53b85d351750cae726ce001cb68afa9c1/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a99eaab34a9010f1a086b126de467466620a750634d114d20455f3a824aae033", size = 5895175, upload-time = "2026-04-20T23:35:33.584Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fd/b82b5601a97630308bef079f545ffec481bbbc795c2ba5ec416a01d03f60/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ffdd7dc5463ccd61845ac37b7012d0f35a1548df9febe14f8dd549be4a0bc81e", size = 4110658, upload-time = "2026-04-20T23:35:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/62/8c/32ca69b0389ef25dd22937bf9e8fbe2ce27aea20b05ded48c4ce4cb42475/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:54a0dfecab1b48731f934e06139dfe11e24219fb6d0ceb32177cf0375f14c7b5", size = 3656251, upload-time = "2026-04-20T23:35:37.854Z" }, + { url = "https://files.pythonhosted.org/packages/c4/29/96992a2b59e3b9d730fcf9612d0a387305025dc867a9fc490a9e496e074e/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:96937c9c5d891f772430f418a7a8b4691a90c3e6b93cf72b5bd7cad8cbca32a5", size = 3301810, upload-time = "2026-04-20T23:35:39.927Z" }, + { url = "https://files.pythonhosted.org/packages/56/ad/44b06659949b243ae10112cd3b20a197f9bf3e81d5651379b9eb889bfaad/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:77b348775efd4cdab410ec6609d81ccecd1139c90265fa583a7255c8064bc03d", size = 3048977, upload-time = "2026-04-20T23:35:41.806Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f2/10a1bcebadb6aa55e280e1f58975c36a7b560ea525184c7aa4064c466633/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:527e6342b3e44c2f0544f6b8e927d60de7f163f5723b8f1dfa7d2a84298738cd", size = 3351466, upload-time = "2026-04-20T23:35:43.993Z" }, + { url = "https://files.pythonhosted.org/packages/20/be/b732c8418ffa5bcfda002890f5dc4c869fc17db66ff11f53b17cfe44afc0/psycopg2_binary-2.9.12-cp314-cp314-win_amd64.whl", hash = "sha256:f12ae41fcafadb39b2785e64a40f9db05d6de2ac114077457e0e7c597f3af980", size = 2848762, upload-time = "2026-04-20T23:35:46.421Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" }, + { url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" }, + { url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" }, + { url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" }, + { url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" }, + { url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" }, + { url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" }, + { url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" }, + { url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" }, + { url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" }, + { url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" }, + { url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" }, + { url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" }, + { url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" }, + { url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" }, + { url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" }, + { url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" }, + { url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" }, + { url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" }, + { url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" }, + { url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" }, + { url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" }, + { url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" }, + { url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" }, + { url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" }, + { url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" }, + { url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" }, + { url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" }, + { url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" }, + { url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" }, + { url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" }, + { url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" }, + { url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" }, + { url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" }, + { url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" }, + { url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" }, + { url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.14.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/60/1d1e59c9c90d54591469ada7d268251f71c24bdb765f1a8a832cee8c6653/pydantic_settings-2.14.1.tar.gz", hash = "sha256:e874d3bec7e787b0c9958277956ed9b4dd5de6a80e162188fdaff7c5e26fd5fa", size = 235551, upload-time = "2026-05-08T13:40:06.542Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/8d/f1af3832f5e6eb13ba94ee809e72b8ecb5eef226d27ee0bef7d963d943c7/pydantic_settings-2.14.1-py3-none-any.whl", hash = "sha256:6e3c7edfd8277687cdc598f56e5cff0e9bfff0910a3749deaa8d4401c3a2b9de", size = 60964, upload-time = "2026-05-08T13:40:04.958Z" }, +] + +[[package]] +name = "pyee" +version = "13.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/04/e7c1fe4dc78a6fdbfd6c337b1c3732ff543b8a397683ab38378447baa331/pyee-13.0.1.tar.gz", hash = "sha256:0b931f7c14535667ed4c7e0d531716368715e860b988770fc7eb8578d1f67fc8", size = 31655, upload-time = "2026-02-14T21:12:28.044Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/b4d4827c93ef43c01f599ef31453ccc1c132b353284fc6c87d535c233129/pyee-13.0.1-py3-none-any.whl", hash = "sha256:af2f8fede4171ef667dfded53f96e2ed0d6e6bd7ee3bb46437f77e3b57689228", size = 15659, upload-time = "2026-02-14T21:12:26.263Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, +] + +[[package]] +name = "pytest-base-url" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/1a/b64ac368de6b993135cb70ca4e5d958a5c268094a3a2a4cac6f0021b6c4f/pytest_base_url-2.1.0.tar.gz", hash = "sha256:02748589a54f9e63fcbe62301d6b0496da0d10231b753e950c63e03aee745d45", size = 6702, upload-time = "2024-01-31T22:43:00.81Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/1c/b00940ab9eb8ede7897443b771987f2f4a76f06be02f1b3f01eb7567e24a/pytest_base_url-2.1.0-py3-none-any.whl", hash = "sha256:3ad15611778764d451927b2a53240c1a7a591b521ea44cebfe45849d2d2812e6", size = 5302, upload-time = "2024-01-31T22:42:58.897Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "pytest-playwright" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "playwright" }, + { name = "pytest" }, + { name = "pytest-base-url" }, + { name = "python-slugify" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/ef/172eb8e23c80491fc72f1401c72f9305663873649351306a38b18406b0c9/pytest_playwright-0.8.0.tar.gz", hash = "sha256:7888d4a2443160c82e0c506c437076679f86b36d1910427250d90fbf1843a981", size = 17132, upload-time = "2026-05-18T10:16:15.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/71/1c545fac6a9054b52b3771238fb2dc6e8f1d0ccec116e1c7786ec191887c/pytest_playwright-0.8.0-py3-none-any.whl", hash = "sha256:856aae6efd4bc055f2ef229c647768760bcaad5cd3a5983c314ac260a974a933", size = 17143, upload-time = "2026-05-18T10:16:18.226Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-discovery" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/60/e88788207d81e46362cfbef0d4aaf4c0f49efc3c12d4c3fa3f542c34ebec/python_discovery-1.3.1.tar.gz", hash = "sha256:62f6db28064c9613e7ca76cb3f00c38c839a07c31c00dfe7ed0986493d2150a6", size = 68011, upload-time = "2026-05-12T20:53:36.336Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/6f/a05a317a66fee0aad270011461f1a63a453ed12471249f172f7d2e2bc7b4/python_discovery-1.3.1-py3-none-any.whl", hash = "sha256:ed188687ebb3b82c01a17cd5ac62fc94d9f6487a7f1a0f9dfe89753fec91039c", size = 33185, upload-time = "2026-05-12T20:53:34.969Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, +] + +[[package]] +name = "python-jose" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ecdsa" }, + { name = "pyasn1" }, + { name = "rsa" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/77/3a1c9039db7124eb039772b935f2244fbb73fc8ee65b9acf2375da1c07bf/python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b", size = 92726, upload-time = "2025-05-28T17:31:54.288Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/c3/0bd11992072e6a1c513b16500a5d07f91a24017c5909b02c72c62d7ad024/python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771", size = 34624, upload-time = "2025-05-28T17:31:52.802Z" }, +] + +[package.optional-dependencies] +cryptography = [ + { name = "cryptography" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.29" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/fe/70bd71a6738b09a0bdf6480ca6436b167469ca4578b2a0efbe390b4b0e70/python_multipart-0.0.29.tar.gz", hash = "sha256:643e93849196645e2dbdd81a0f8829a23123ad7f797a84a364c6fb3563f18904", size = 45678, upload-time = "2026-05-17T17:29:47.654Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/cb/769cfc37177252872a45a71f3fbdde9d51b471a3f3c14bfe95dde3407386/python_multipart-0.0.29-py3-none-any.whl", hash = "sha256:2ddcc971cef266225f54f552d8fa10bcfbb1f14446caec199060daac59ff2d69", size = 29640, upload-time = "2026-05-17T17:29:45.69Z" }, +] + +[[package]] +name = "python-slugify" +version = "8.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "text-unidecode" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/c7/5e1547c44e31da50a460df93af11a535ace568ef89d7a811069ead340c4a/python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856", size = 10921, upload-time = "2024-02-08T18:32:45.488Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/62/02da182e544a51a5c3ccf4b03ab79df279f9c60c5e82d5e8bec7ca26ac11/python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", size = 10051, upload-time = "2024-02-08T18:32:43.911Z" }, +] + +[[package]] +name = "pytokens" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/34/b4e015b99031667a7b960f888889c5bd34ef585c85e1cb56a594b92836ac/pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a", size = 23015, upload-time = "2026-01-30T01:03:45.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/5d/e44573011401fb82e9d51e97f1290ceb377800fb4eed650b96f4753b499c/pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083", size = 160663, upload-time = "2026-01-30T01:03:06.473Z" }, + { url = "https://files.pythonhosted.org/packages/f0/e6/5bbc3019f8e6f21d09c41f8b8654536117e5e211a85d89212d59cbdab381/pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1", size = 255626, upload-time = "2026-01-30T01:03:08.177Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3c/2d5297d82286f6f3d92770289fd439956b201c0a4fc7e72efb9b2293758e/pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1", size = 269779, upload-time = "2026-01-30T01:03:09.756Z" }, + { url = "https://files.pythonhosted.org/packages/20/01/7436e9ad693cebda0551203e0bf28f7669976c60ad07d6402098208476de/pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9", size = 268076, upload-time = "2026-01-30T01:03:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/2e/df/533c82a3c752ba13ae7ef238b7f8cdd272cf1475f03c63ac6cf3fcfb00b6/pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68", size = 103552, upload-time = "2026-01-30T01:03:12.066Z" }, + { url = "https://files.pythonhosted.org/packages/cb/dc/08b1a080372afda3cceb4f3c0a7ba2bde9d6a5241f1edb02a22a019ee147/pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b", size = 160720, upload-time = "2026-01-30T01:03:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/64/0c/41ea22205da480837a700e395507e6a24425151dfb7ead73343d6e2d7ffe/pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f", size = 254204, upload-time = "2026-01-30T01:03:14.886Z" }, + { url = "https://files.pythonhosted.org/packages/e0/d2/afe5c7f8607018beb99971489dbb846508f1b8f351fcefc225fcf4b2adc0/pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1", size = 268423, upload-time = "2026-01-30T01:03:15.936Z" }, + { url = "https://files.pythonhosted.org/packages/68/d4/00ffdbd370410c04e9591da9220a68dc1693ef7499173eb3e30d06e05ed1/pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4", size = 266859, upload-time = "2026-01-30T01:03:17.458Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c9/c3161313b4ca0c601eeefabd3d3b576edaa9afdefd32da97210700e47652/pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78", size = 103520, upload-time = "2026-01-30T01:03:18.652Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a7/b470f672e6fc5fee0a01d9e75005a0e617e162381974213a945fcd274843/pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321", size = 160821, upload-time = "2026-01-30T01:03:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/80/98/e83a36fe8d170c911f864bfded690d2542bfcfacb9c649d11a9e6eb9dc41/pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa", size = 254263, upload-time = "2026-01-30T01:03:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/0f/95/70d7041273890f9f97a24234c00b746e8da86df462620194cef1d411ddeb/pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d", size = 268071, upload-time = "2026-01-30T01:03:21.888Z" }, + { url = "https://files.pythonhosted.org/packages/da/79/76e6d09ae19c99404656d7db9c35dfd20f2086f3eb6ecb496b5b31163bad/pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324", size = 271716, upload-time = "2026-01-30T01:03:23.633Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/482e55fa1602e0a7ff012661d8c946bafdc05e480ea5a32f4f7e336d4aa9/pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9", size = 104539, upload-time = "2026-01-30T01:03:24.788Z" }, + { url = "https://files.pythonhosted.org/packages/30/e8/20e7db907c23f3d63b0be3b8a4fd1927f6da2395f5bcc7f72242bb963dfe/pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb", size = 168474, upload-time = "2026-01-30T01:03:26.428Z" }, + { url = "https://files.pythonhosted.org/packages/d6/81/88a95ee9fafdd8f5f3452107748fd04c24930d500b9aba9738f3ade642cc/pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3", size = 290473, upload-time = "2026-01-30T01:03:27.415Z" }, + { url = "https://files.pythonhosted.org/packages/cf/35/3aa899645e29b6375b4aed9f8d21df219e7c958c4c186b465e42ee0a06bf/pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975", size = 303485, upload-time = "2026-01-30T01:03:28.558Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/07907b6ff512674d9b201859f7d212298c44933633c946703a20c25e9d81/pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a", size = 306698, upload-time = "2026-01-30T01:03:29.653Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/cbbf9250020a4a8dd53ba83a46c097b69e5eb49dd14e708f496f548c6612/pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918", size = 116287, upload-time = "2026-01-30T01:03:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/c6/78/397db326746f0a342855b81216ae1f0a32965deccfd7c830a2dbc66d2483/pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de", size = 13729, upload-time = "2026-01-30T01:03:45.029Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "regex" +version = "2026.5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/0e/49aee608ad09480e7fd276898c99ec6192985fa331abe4eb3a986094490b/regex-2026.5.9.tar.gz", hash = "sha256:a8234aa23ec39894bfe4a3f1b85616a7032481964a13ac6fc9f10de4f6fca270", size = 416074, upload-time = "2026-05-09T23:15:19.37Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/9b/6550044bc44e17c84d312c031c2ec42fbdb6a4ec4e29093be3a172d08772/regex-2026.5.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57eeeb05db7979413dec5438f2db21d7ecbba787cde7a711df1a6f6df672aa06", size = 490451, upload-time = "2026-05-09T23:12:34.72Z" }, + { url = "https://files.pythonhosted.org/packages/1e/95/fc7ba4303b5a0f92446a12ee6778ef2c6c799233f5060042a31bf390cfe9/regex-2026.5.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:398c521292f4c7fb807001dcd54694d3a1fcafc179a36ad9cc56f98df85930b6", size = 292112, upload-time = "2026-05-09T23:12:36.285Z" }, + { url = "https://files.pythonhosted.org/packages/54/4b/ee27938d1b2c443e89a9a10e00d2d19aa5ee300cd3d61140644e93bb083e/regex-2026.5.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7a7c26137296beba7784de6eba69c6a93a63ccebc385e4962fe67e267a91225", size = 289599, upload-time = "2026-05-09T23:12:38.089Z" }, + { url = "https://files.pythonhosted.org/packages/d8/dd/ba103dc19614e25f3880800ca67ce093d6e21b325d72b8383c7bf906e9fa/regex-2026.5.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6441cc660d76107934a09c22167200839a0e89604a6297f78a974e66e931d2c0", size = 796732, upload-time = "2026-05-09T23:12:40.062Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e7/f035b4fd858b050b0080bf302968dc0f59ba34e391872d54936758e6844e/regex-2026.5.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91328f1c23d47595ca3ef0a7557fa129c5a23404b775c770697d2f35b33e0107", size = 865440, upload-time = "2026-05-09T23:12:42.059Z" }, + { url = "https://files.pythonhosted.org/packages/0a/51/8cd301ecc899aea28124357f729f4272f44de7806fc7ca02490bfbe253e8/regex-2026.5.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:93a7860539414dddaefba2b40f8771765ae17949d4c7182b876ce429e11a8309", size = 912329, upload-time = "2026-05-09T23:12:44.373Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1e/3fbe2fa1e8cebd62f3bb7d3321cff1640aca2e240b51d9bd624aad949260/regex-2026.5.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd2810d22146b6d838acc5ec15602cb6b47920aa4e33015df3868eedfd20bab8", size = 801239, upload-time = "2026-05-09T23:12:46.268Z" }, + { url = "https://files.pythonhosted.org/packages/17/2f/6f6008682bf2cf98040a0d3153a8e557b6ab728d7713d045cee4ce544ab8/regex-2026.5.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daff2bdbaf1d23e52fdff7c0b7bc2048b68f978df6a4d107ac981f94caef2e66", size = 777054, upload-time = "2026-05-09T23:12:48.051Z" }, + { url = "https://files.pythonhosted.org/packages/19/2b/eee0d20a6842ba04df4b8847a920b57ef56853f14ef85405473e586b605a/regex-2026.5.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4eeb011098fcb77af513dcef521a3dbecbf8849b1e38940759d293b7a93f5026", size = 785098, upload-time = "2026-05-09T23:12:49.851Z" }, + { url = "https://files.pythonhosted.org/packages/4a/98/6fc1e6410feefb92159edaed5041992bfe390e8d26c721865434acbca558/regex-2026.5.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ea9c8ecfa1b73c73b626534d6626e5340d429630943672b8480724f44e84b962", size = 860095, upload-time = "2026-05-09T23:12:51.666Z" }, + { url = "https://files.pythonhosted.org/packages/18/a3/bd855e0f2cb1a978ecf6fa6bb69632dd9c3f6ea3b81cde62fde14c9daec7/regex-2026.5.9-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cd2846168eb9ee3c513902bc8225409cb1caab31d04728b145171fa1625d9621", size = 765762, upload-time = "2026-05-09T23:12:53.413Z" }, + { url = "https://files.pythonhosted.org/packages/dc/66/0ae8c092e60b14c79d24f8e0b7f0aea5bfbffdcab00b5483d13404d3c3a5/regex-2026.5.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39617fb0cde9c0e6306dc70e3bfc096f3da793219879f7ae7aa341a69fbdcf6d", size = 852100, upload-time = "2026-05-09T23:12:55.256Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/8dfde60fc1b21c946a893ba273403b72617edb261370cb1087099a83f088/regex-2026.5.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fd03c4f0e33280d15cae17159b899245d6b7c53d21def19b263b39655061f5ce", size = 789479, upload-time = "2026-05-09T23:12:57.573Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1c/bdcc98f9a4af4fdd166c74941174619ccff4726d3ce32faa8e9a2ecd38dd/regex-2026.5.9-cp312-cp312-win32.whl", hash = "sha256:164eba9b755ea6f244b0d881196fbc1fac09714e9782c9e2732b813142033c8e", size = 266699, upload-time = "2026-05-09T23:12:59.14Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/240d36864f9e48ace85f72e79ced97ceb7f27ce87739a947dcb834b4e6bc/regex-2026.5.9-cp312-cp312-win_amd64.whl", hash = "sha256:86f40a5d6444db30a125c9c9177e6b25dad981cbc37451fd838f145e6edac92e", size = 277783, upload-time = "2026-05-09T23:13:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b5/7b30f312b0669dff5beebe5b0989dc2d1a312b1a44fab852199c387a5b96/regex-2026.5.9-cp312-cp312-win_arm64.whl", hash = "sha256:96f5f58b54a063d7ea9dca08e1cf57bfe10499c4d579ee672da284f57f5f0070", size = 270513, upload-time = "2026-05-09T23:13:02.426Z" }, + { url = "https://files.pythonhosted.org/packages/aa/da/797e91ecec6f84135da778ddce78c20e0af5d2a15c26f87a81bc3eadb6db/regex-2026.5.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d626b84406444b165fc0ba981604edea39f0588ff1f92baa23fe50799ea9afdb", size = 490303, upload-time = "2026-05-09T23:13:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/44/da/bf30abaaa737b58f4a4b8c4a03659e02fd92092c822e0197ed9e0daab917/regex-2026.5.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d7bdc0ab8f3dd7e1b4f9ab88634e13374669db86bb3c72e8292f07ae313f539f", size = 292019, upload-time = "2026-05-09T23:13:06.022Z" }, + { url = "https://files.pythonhosted.org/packages/2d/e7/d0eaf5713828417b9e5648cf81fa9bacd4961f6ab98c380c2034f8716e35/regex-2026.5.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a8820737949116ffff55fe18f9fc644530063ba6ebfcb8314239416e78f1347c", size = 289468, upload-time = "2026-05-09T23:13:08.214Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9b/b3fdd62b003baa1a9b593cd8c8699c9651c2e80cc21a5c715707983c42d7/regex-2026.5.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0fbdbac82cb3e4450d0ccde7d7a35607f4cb2dd9fba4b8b69bfaf8c9fa6aed", size = 796749, upload-time = "2026-05-09T23:13:10.573Z" }, + { url = "https://files.pythonhosted.org/packages/d4/30/66ab84588765f5b4b271a9ca09ef7ce2b87caa95176ec3d2ad65d7bc4902/regex-2026.5.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57e8915c7986aa33d25e4d3629cef711cd2863f2961b10409f0c04cb8b7d9020", size = 865445, upload-time = "2026-05-09T23:13:12.523Z" }, + { url = "https://files.pythonhosted.org/packages/1a/89/f05169e8588aac365f35ffc7f3bc3184f095ef4cfded7cfaa3c7fd5dbd89/regex-2026.5.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:508f56a89ba9cb26e4168cbc37dbd60a28d82430a9e18ad1d25fe0883c314ca2", size = 912322, upload-time = "2026-05-09T23:13:14.281Z" }, + { url = "https://files.pythonhosted.org/packages/30/e1/c93444052cf41581f3c884ab3fb5823daf0992f11cd4388d4275ca610558/regex-2026.5.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d189041f15691cfa2b6c4290448ec221244d225b3f5fe9e7771b34ffcdf6e2", size = 801269, upload-time = "2026-05-09T23:13:16.569Z" }, + { url = "https://files.pythonhosted.org/packages/50/fe/0cf96b882f540e62e8b9956599798203d599c44cf4c77917ca27400ff69b/regex-2026.5.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e82db382b44d0111b22601c509c89f64434816c9e0eef9d1989cda8cc6ff1c04", size = 777085, upload-time = "2026-05-09T23:13:18.675Z" }, + { url = "https://files.pythonhosted.org/packages/23/5c/d78d4924e7fc875557b9e9b768423925fdfaac5549d06da7810019a9bd26/regex-2026.5.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2acfb48634f64996b57f90f39afa692ff362162722581921fe92239a59960f3c", size = 785153, upload-time = "2026-05-09T23:13:20.525Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e0/5214774090e7b4524dcea3e3c4aa74141d43043f8beb49c1599db1c8b53a/regex-2026.5.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d29eebfc9525db68cad3c97eedd7f754fa265aa5cd0cf4f863b2421e1b48fc9f", size = 860164, upload-time = "2026-05-09T23:13:22.263Z" }, + { url = "https://files.pythonhosted.org/packages/6e/e1/4a57a83350319b1271f0d7a249b8672513ed928b237a741631270de6caea/regex-2026.5.9-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:debb893095e944091c16e641a6e33c1b0f4cb61ab945ec5afbf53ce7068834d8", size = 765731, upload-time = "2026-05-09T23:13:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/12/f4/499e74a20c156fc75836ee04a72a38d1a063978f600937f9760467beb1b0/regex-2026.5.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d659eee77986549c9ea45b861c7567e44d6287c3dc9a4565478853f7b9fe2ff6", size = 852062, upload-time = "2026-05-09T23:13:26.125Z" }, + { url = "https://files.pythonhosted.org/packages/5b/92/7eebc0d0a01e78629695f342ba17e0deaff8fb45e79cc0d7b98287da6e3e/regex-2026.5.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2efa205e6d98b24d1f3ab395c11aa15cdf10935bca283d0285e0499c284fba21", size = 789577, upload-time = "2026-05-09T23:13:27.814Z" }, + { url = "https://files.pythonhosted.org/packages/05/a4/018e71f7d2ad48c1ebe6d3ae0026f9b7cb4802fd15c7cc02fdf724355102/regex-2026.5.9-cp313-cp313-win32.whl", hash = "sha256:f3844f134e834076677dd369976e9f5068679fcb8e50102fdf6b7ac96a3ec127", size = 266691, upload-time = "2026-05-09T23:13:29.549Z" }, + { url = "https://files.pythonhosted.org/packages/e6/1d/861a93719fb9ee7dbfc3761b3797b7a3e112a5d42c6129459d2d741be9b5/regex-2026.5.9-cp313-cp313-win_amd64.whl", hash = "sha256:3527bb4942d2c14552155406cdedd906567456821848aed1cb4933a391bf5eca", size = 277747, upload-time = "2026-05-09T23:13:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/d9/c6/0a2436ae4da1ba76e51cb98943c6838a9a721faa40ebe2dce07694ae34e3/regex-2026.5.9-cp313-cp313-win_arm64.whl", hash = "sha256:56a33f191f17d8c417f99945ebdc1e691d3af9605d86ec68c7e54a57e3e17af6", size = 270500, upload-time = "2026-05-09T23:13:33.525Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e9/d21346f7b60ed58789371358ed66b09d00f832e1bd7c06e55d9da5679882/regex-2026.5.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:01f28d868834624c934b8d2e0aa1c8341337e37831f4a012f18a5afcba4cbaf3", size = 494172, upload-time = "2026-05-09T23:13:35.935Z" }, + { url = "https://files.pythonhosted.org/packages/c4/43/fd1177a2032037c681baecdb3422ee4e1424aec4e4f470ef47793d325274/regex-2026.5.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:48036f6374aaa79eb3b754ec29c61d1c6b1606749d705a13f8854fa2539671f6", size = 293952, upload-time = "2026-05-09T23:13:38.307Z" }, + { url = "https://files.pythonhosted.org/packages/f2/7d/9fbf919768368d3f8a4f6c692cf2aa61e482b2b81ec6a298ace4cbf02480/regex-2026.5.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b96350aa424e79d4fd6b567b344dcbe2b2d6bfc48dfe7717587e1fa6d43da6ff", size = 292314, upload-time = "2026-05-09T23:13:40.353Z" }, + { url = "https://files.pythonhosted.org/packages/e2/6c/e41bfeecb589716843e7c4df09ba46ff2a42961457afece19059d85caeef/regex-2026.5.9-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8f3af7a4903c5c04a11a196a5aa75cdd7dd3f8508132f9fb3259d9f5908e3b88", size = 811681, upload-time = "2026-05-09T23:13:42.543Z" }, + { url = "https://files.pythonhosted.org/packages/87/83/a5c1c525fba0aa656e88ad0face0b1829788ef4c2fb6b26df58aa1151b84/regex-2026.5.9-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e87577720152d2caae19fe2baaf1f8d5ca12091e9e229f03915c37d1e4b9178", size = 871135, upload-time = "2026-05-09T23:13:44.326Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/80882e799e440dd878b0979cbebf8fa4d54624a332c83037c7a701649e3f/regex-2026.5.9-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c8b9b9d294cfea3cd19c718ade7cc93492b2c4991abd9a68d0b3477ae6d8e100", size = 917265, upload-time = "2026-05-09T23:13:47.295Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ff/8db60211e2286e396aad7dc7725356c502bff0901ea05bd6cdc2e1a042b9/regex-2026.5.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:728d8bfd28a8845c8b6bc5dc7ce010453d206396786c0765c2740cb65f37791e", size = 816311, upload-time = "2026-05-09T23:13:49.885Z" }, + { url = "https://files.pythonhosted.org/packages/4c/47/742ef579c61730f8d268e5cf1f9ce0e37e2ea041ad0f5644724f2378e463/regex-2026.5.9-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7e30b874d341fac767d7df5a0870540541c2c054b80cfaac116e8d367a8a7ff2", size = 785498, upload-time = "2026-05-09T23:13:52.25Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ab/cb0999802dcb0fb95b1ab005e8d4163d8afdd67efc2cb6b6630ac13f8cb1/regex-2026.5.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b", size = 801348, upload-time = "2026-05-09T23:13:54.127Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/8ca59a24c55bc34d166eefaf3717bd77772f329fdbf984d86581e0a3571c/regex-2026.5.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8e76e8161ad00694cfce6767d5dea860c6391ac5b83e5c3a39661e696f11fc7e", size = 866493, upload-time = "2026-05-09T23:13:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3d/30f2ae62cef3278bb5bb821f467277a55fb73f01032cf85997e15e8289a8/regex-2026.5.9-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ddda5340e6c01a293027dd46232fa79eaff1b48058ce7a98f572b6445b088041", size = 772811, upload-time = "2026-05-09T23:13:57.867Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ae/7d2089bcd78ad0c0161bc684339df50032acb438a7bd3305e7ddb1193cec/regex-2026.5.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:205109e96b3cf5adf8f4cd62bedde9487feb282b9497a3535451e5a24cd706a0", size = 856584, upload-time = "2026-05-09T23:13:59.679Z" }, + { url = "https://files.pythonhosted.org/packages/a9/29/92ff47f75990131ea4f24ba17819e5a9d141e10819807e09addd73409af6/regex-2026.5.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dfbe4579b9f08036aa7d101d1835437a20783574ac66327e6b29b4018a138081", size = 803453, upload-time = "2026-05-09T23:14:01.978Z" }, + { url = "https://files.pythonhosted.org/packages/04/99/eff29f1037dcab36702c9ee5d6858cf1ce2336ea8ea2987f64245b99ea5e/regex-2026.5.9-cp313-cp313t-win32.whl", hash = "sha256:ed2c9e8068b614c574d8d30e543d617cf5379b0535d46f97ef00e904745a08b5", size = 269951, upload-time = "2026-05-09T23:14:03.661Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9d/8870b8981d27b22cda77bb26a5ac7ebfa9c7d9e0dea195a834a82380e748/regex-2026.5.9-cp313-cp313t-win_amd64.whl", hash = "sha256:b46b0f094dc1d3b90356c85a0bd2c9bafc4a6a190b9d6f8ddd5a033b6e088ed4", size = 281240, upload-time = "2026-05-09T23:14:05.56Z" }, + { url = "https://files.pythonhosted.org/packages/72/b1/3379415e8f135c13ac551353397cc4fe97b4978f3cac73c5fcbcded548b8/regex-2026.5.9-cp313-cp313t-win_arm64.whl", hash = "sha256:872acc074bd29ffc9913ecdfedf6ea77502312ca44a4aa0d3779089c6069d8de", size = 272383, upload-time = "2026-05-09T23:14:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/9c3cd292d8808b3645a2ce517e200179b6d0e903f176300bd8b542e14de5/regex-2026.5.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:1bd7587a2948b4085195d5a3374eaf4a425dc3e55784c038175355ecf3bbbf8a", size = 490376, upload-time = "2026-05-09T23:14:09.64Z" }, + { url = "https://files.pythonhosted.org/packages/60/70/d43ee8a2ca0a8b68d167f21658b85520ac0574617c7f320367c5047f7556/regex-2026.5.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:dea2e88e1cce4522496cce630e11e67b98b7076620bc4336c3f674bc21a375f4", size = 291964, upload-time = "2026-05-09T23:14:11.424Z" }, + { url = "https://files.pythonhosted.org/packages/21/91/9d50b433828d8e74196904e168a43abf1e6e88b2a15d47ed742456720c37/regex-2026.5.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2099f7e7ff7b6aa3192312650a56e91cc091e49d50b04e4f6f8b6e28b3b27f1c", size = 289682, upload-time = "2026-05-09T23:14:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/b835e3cafbb9d977736912436259ff551d60919f7d7b3d37d46659c63564/regex-2026.5.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecd353045824e4477562a2ac718c25799cdaaa41f7aa925a806a8a3e6848a5b9", size = 796996, upload-time = "2026-05-09T23:14:14.923Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a6/9f992d00019166b9de01c546dd4549bc679f2a68df11b877740b0760b7c2/regex-2026.5.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65c8c8c37377794bd5b2f3ebe51919042bf17aec802e23c833d89782ed0c78af", size = 866089, upload-time = "2026-05-09T23:14:17.757Z" }, + { url = "https://files.pythonhosted.org/packages/e0/08/4d32af657e049b19cb62b02e46e38fe1518797bfb2203ee93a510b21b0dc/regex-2026.5.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b73ab8afcf66c622db143d1c6fda4e58e4d537ee4f125229ad47b1ab80f34c0", size = 911530, upload-time = "2026-05-09T23:14:20.353Z" }, + { url = "https://files.pythonhosted.org/packages/d9/27/2af43dd1dc201d1fecefda64a45f4ad0995855b92724f795a777b402ee69/regex-2026.5.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0de5cf193997384ed2ca6f1cd4f78055b255d93d82d5a8cd6ba0d11c10b167e4", size = 800643, upload-time = "2026-05-09T23:14:22.265Z" }, + { url = "https://files.pythonhosted.org/packages/a4/dd/23a249047013b5321d4a60c4d2437462086f601b061776a525e5fba2a59f/regex-2026.5.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d641a8c9a61618047796d572a39a79b26167b0411d2c3031937b2fe2d081e2cf", size = 777223, upload-time = "2026-05-09T23:14:24.179Z" }, + { url = "https://files.pythonhosted.org/packages/94/6a/e85ed9538cd19586d0465076a4578a12e093ce776d15f3f8ce92733a8dd6/regex-2026.5.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:24b2355ef5cc9aa5b8f07d17704face1c166fdcc2290fa7bd6e6c925655a8346", size = 785760, upload-time = "2026-05-09T23:14:26.065Z" }, + { url = "https://files.pythonhosted.org/packages/2a/c4/f25473209438638e947c55f9156fd8f236f74169229028cc99116380868e/regex-2026.5.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a24852d3c29ad9e47593593d8a247c44ccc3d0548ef12c822d6ed0810affe676", size = 860891, upload-time = "2026-05-09T23:14:28.17Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f7/f4f86e3c74419c37370e91f150ae0c2ef7d34b2e0e4cdd5da046a02e4022/regex-2026.5.9-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:916714069da19329ef7de197dcbc77bb3104145c7c2c864dbfbe318f46b88b14", size = 765891, upload-time = "2026-05-09T23:14:30.06Z" }, + { url = "https://files.pythonhosted.org/packages/26/70/704d8e13765939146b1cd0ef4e2feb71d7929727d2290f026eed10095955/regex-2026.5.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fa411799ca8da32a8d38d020a88faa5b6f91657d284761352940ecf9f7c3bbdd", size = 851380, upload-time = "2026-05-09T23:14:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/26/29/1a13582a8460038edc38e49f64ceb0dd7c60f5caba77571f4bf6601965d9/regex-2026.5.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e6da47d679b7010ef27556b6e0f99771b744936db1792a10ceac6547ae1503e", size = 789350, upload-time = "2026-05-09T23:14:34.799Z" }, + { url = "https://files.pythonhosted.org/packages/73/56/3dcafe34fc72e271d62ad9a291801e88a1457bb251c132f15fcc2e5aad1a/regex-2026.5.9-cp314-cp314-win32.whl", hash = "sha256:98bd73080e8756255137e1bd3f3f00295bbc5aa383c0e0f973920e9134d7c4ad", size = 272130, upload-time = "2026-05-09T23:14:36.729Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9c/02eebf0be95efe416c664db7fb8b6b05b7a0b06a7544f2884f2558b0526f/regex-2026.5.9-cp314-cp314-win_amd64.whl", hash = "sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763", size = 280999, upload-time = "2026-05-09T23:14:39.126Z" }, + { url = "https://files.pythonhosted.org/packages/70/5a/1dd1abee76cb7a846a0bcf42fdc87e5720c3c33c24f3e37814310a513d9f/regex-2026.5.9-cp314-cp314-win_arm64.whl", hash = "sha256:e1d93bf647916292e8edcec150c07ddf3dc50179ccaf770c04a7f9e452155372", size = 273500, upload-time = "2026-05-09T23:14:41.059Z" }, + { url = "https://files.pythonhosted.org/packages/86/c1/c5f619b0057a7965cb78ec559c1d7a45ce8c99a35bea95483d64959a93d9/regex-2026.5.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:83d0ee4a57d1c87cb549e195ec300b8f0ec3a82eba66d835e4e2ed8634fe4499", size = 494269, upload-time = "2026-05-09T23:14:42.869Z" }, + { url = "https://files.pythonhosted.org/packages/05/2c/5d01f1aee33de4bbe60c8452945bfc8477ca7c5ae4450f6bfe711036cb36/regex-2026.5.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d3d7eb5c9a7f6df82ed3cfac9beb93882a5cbcb5b8b157b56cb2b3b276574ac1", size = 293954, upload-time = "2026-05-09T23:14:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fe/e8988b2ae2108c6ef71bd4aa8d87fbe257976dd0810e826cd75f701c68b6/regex-2026.5.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:075160bf16658e16d35233300b8453aac25de4cbea808d22348b6979668e924d", size = 292405, upload-time = "2026-05-09T23:14:47.211Z" }, + { url = "https://files.pythonhosted.org/packages/79/34/d2b0937faa7859263f7f0a3c6b103a1296306be6952dc173d0154e9a2f49/regex-2026.5.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45375819235558a4ff1c4971dc32881f022613abdb180128f5cb4768c1765a1c", size = 811855, upload-time = "2026-05-09T23:14:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/80/fe/daf53a47457a8486db66c66c01ceb9c2303eecee3f87197f1e77eb1a736d/regex-2026.5.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ead4b163ac30a29574510cd4b3e2e985ac5290c05fc7095557d6a5f403fc31b5", size = 871189, upload-time = "2026-05-09T23:14:51.555Z" }, + { url = "https://files.pythonhosted.org/packages/1c/75/058fc4470cbfbf57d800aff1a0022b929a3f9fa553ee10a0cdf2070eb31f/regex-2026.5.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c6e4218fbdfbcd4f6c19efca40930d24a621bf4b48cb76bc6640543bd28ef20", size = 917485, upload-time = "2026-05-09T23:14:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/88/e7/179cfda3a28bc843b5c6cfe7f79f23489c791ed95f151083803660878432/regex-2026.5.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6351571c8a42b505eb555c0dc47d740d0fb66977dc142919eea6f4325b7c56a0", size = 816369, upload-time = "2026-05-09T23:14:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/41/90/6f0cc422071688266d344fca8462d787cba0a2c144acb25721f9a61ec265/regex-2026.5.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:002205cafd2a9e78c6290c7d1df277bf3277b3b7a30e0b4bb0dac2e2e3f7cb2d", size = 785869, upload-time = "2026-05-09T23:14:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/02/67/a31f1760f09c27b251ef39e9beb541f462cf977381d067faa764c2c0e393/regex-2026.5.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8abd33fef90b2a9efac5557d6033ca82d1195ed3a15fea5af15ba7b463c6a63b", size = 801427, upload-time = "2026-05-09T23:15:00.642Z" }, + { url = "https://files.pythonhosted.org/packages/e3/c4/1a80654597b6bc1e1ea0494824c31200e8a956abe290afae9b19a166a148/regex-2026.5.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:31037c82eccb44b7ea2e9e221d7c01429430e989a1f4b91ea5a855f6017b509a", size = 866482, upload-time = "2026-05-09T23:15:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/960724e06482c08466ff5611e242e86f80062949cdf6b4b9cc317b9dd93d/regex-2026.5.9-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5604dfd046dc37eca90250fc3be938b076c8059fa772ac0ed6f499b0f0fb0415", size = 773022, upload-time = "2026-05-09T23:15:05.625Z" }, + { url = "https://files.pythonhosted.org/packages/50/a8/a9979c3e7918280e93159ebcab5ef1a65116dd4f3bd6091be0eae4a126e8/regex-2026.5.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e1b1b4e496afbb24f4a62aba855ee4f88f25578927697b340702e48c9ee6bc2", size = 856642, upload-time = "2026-05-09T23:15:07.966Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d4/a9b732f2f0072c0ab12227483abb24fffcb9f73f8a2b203df0a6d0434735/regex-2026.5.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be3372b9df6ddecff6486d37e19095a7b4973137caf5512407a89f4455361f41", size = 803552, upload-time = "2026-05-09T23:15:10.215Z" }, + { url = "https://files.pythonhosted.org/packages/d5/fe/1b3113817447a1d4155e4ac76d2e072f42c0bcba2f43fa8a0e756ea2cd91/regex-2026.5.9-cp314-cp314t-win32.whl", hash = "sha256:3ddd90103f9e5c471c49c7852ecc1fe27c7e45eb99e977aefe7caa4e779f4f58", size = 275746, upload-time = "2026-05-09T23:15:12.609Z" }, + { url = "https://files.pythonhosted.org/packages/92/73/93d42045302636c91f2e5ef588b65b84b01428f28ec77de256b1dfdfbe5c/regex-2026.5.9-cp314-cp314t-win_amd64.whl", hash = "sha256:ca518ed29c46eecba6010b15f1b9a479314d2de409536e71b6a13aa04e3b8a77", size = 285685, upload-time = "2026-05-09T23:15:15.086Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/35b4c33c804a165a7f55289afda3ea9e3eb6d15800341a2d66455c0f1f30/regex-2026.5.9-cp314-cp314t-win_arm64.whl", hash = "sha256:5e41809d2683fcde7d5a8c87a6567ba1fb1ce0de9f31bff578de00a4b2d76daa", size = 275713, upload-time = "2026-05-09T23:15:16.98Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "requests-mock" +version = "1.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/32/587625f91f9a0a3d84688bf9cfc4b2480a7e8ec327cefd0ff2ac891fd2cf/requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401", size = 60901, upload-time = "2024-03-29T03:54:29.446Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/ec/889fbc557727da0c34a33850950310240f2040f3b1955175fdb2b36a8910/requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563", size = 27695, upload-time = "2024-03-29T03:54:27.64Z" }, +] + +[[package]] +name = "responses" +version = "0.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/58/1fb6de3503428196df78638f991ec8095274f1ee9723e272ee4d9ff0092b/responses-0.26.1.tar.gz", hash = "sha256:2eb3218553cc8f79b57d257bac23af5e1bf381f5b9390b1767816f0843e01dc2", size = 83088, upload-time = "2026-05-21T19:56:39.747Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/31/6a620b4427d546b9e7cca8b3b8c5f0559d9cef2bb9eedcda7f73c1473c19/responses-0.26.1-py3-none-any.whl", hash = "sha256:8aacc4586eb08fb2208ef64a9eb4258d9b0c6e6f4260845f2f018ab847495345", size = 35502, upload-time = "2026-05-21T19:56:38.046Z" }, +] + +[[package]] +name = "rich" +version = "14.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/67/cae617f1351490c25a4b8ac3b8b63a4dda609295d8222bad12242dfdc629/rich-14.3.4.tar.gz", hash = "sha256:817e02727f2b25b40ef56f5aa2217f400c8489f79ca8f46ea2b70dd5e14558a9", size = 230524, upload-time = "2026-04-11T02:57:45.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/76/6d163cfac87b632216f71879e6b2cf17163f773ff59c00b5ff4900a80fa3/rich-14.3.4-py3-none-any.whl", hash = "sha256:07e7adb4690f68864777b1450859253bed81a99a31ac321ac1817b2313558952", size = 310480, upload-time = "2026-04-11T02:57:47.484Z" }, +] + +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, +] + +[[package]] +name = "rsa" +version = "4.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/8a/22b7beea3ee0d44b1916c0c1cb0ee3af23b700b6da9f04991899d0c555d4/rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75", size = 29034, upload-time = "2025-04-16T09:51:18.218Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/8d/0133e4eb4beed9e425d9a98ed6e081a55d195481b7632472be1af08d2f6b/rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762", size = 34696, upload-time = "2025-04-16T09:51:17.142Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/8a/8bce2894573e9dae6ff4d77fe34ad727d79b9e6238ad288c5638990d90f6/ruff-0.15.14.tar.gz", hash = "sha256:48e866b165be4a9bdbf310f7d3c9a07edef2fe8cd63ffeb4e00bb590506ebf9f", size = 4700910, upload-time = "2026-05-21T14:34:55.177Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/c8/74a92c6ff9fcfb4f1f947126d3ebee8389276e161ecc85de5bda7cda51bd/ruff-0.15.14-py3-none-linux_armv6l.whl", hash = "sha256:8dd2db9416e487c8d4b01fa7056bb02c4d05969d4f8d17a08c229c2f4ff3c108", size = 10739177, upload-time = "2026-05-21T14:34:37.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/254a35c20acc38a7223c9d2d594af12e794432464f2cdeb52af1dc4a892d/ruff-0.15.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:be4ff55af755bd71a00ab3dc6bd7ffc467bd76e0df6881e286c2e3d23e8fb43b", size = 11144969, upload-time = "2026-05-21T14:34:43.978Z" }, + { url = "https://files.pythonhosted.org/packages/56/9e/d13e40f83b8d0a94430e6778ce1d94a43b38cf2efe63278bdd2b4c65abbf/ruff-0.15.14-py3-none-macosx_11_0_arm64.whl", hash = "sha256:48d5909d7d06276ce7dde6d32bfa4b0d4cb2651145cd8ee4b440722cbc77832f", size = 10478207, upload-time = "2026-05-21T14:34:48.378Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f1/b15a7839fa4f332f8acec78e20564f26bb2d866e3d21710b877fd0263000/ruff-0.15.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca8cbfa94c4f90984a67561978602746d4cd27103568f745fa90eee3f0d4107d", size = 10818459, upload-time = "2026-05-21T14:34:22.318Z" }, + { url = "https://files.pythonhosted.org/packages/45/33/53d651177f84f94b400a0e27f8824eeada3dddc9d5ee8aeb048f4352a520/ruff-0.15.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a6bbc0333f1ab053423bcbf6226477d266ca7cec7738c4c8e3f55647803f3c4", size = 10541800, upload-time = "2026-05-21T14:34:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a6/868f87e0bf9786ed24b5d0d0ad8676b8a94fd1912f42cddf9cfc7857818a/ruff-0.15.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a24a4f7605d7003a6674d4387651effd939dead3fddd0f36561eb77a9a2e542", size = 11342149, upload-time = "2026-05-21T14:34:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/a7/8b/38cd5c19faffdcc05a408d2b78edccc69492ab9720eadb49ea15ef80d768/ruff-0.15.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:049b5326e53ed80978f2fc041a280603f69dd6b0c95464342a2bb4572d9d9e2f", size = 12212563, upload-time = "2026-05-21T14:34:28.579Z" }, + { url = "https://files.pythonhosted.org/packages/3e/4d/a3c5b874a556d5731e3e657aaf04311bb76f0a5c3ec220ed43051be6b64b/ruff-0.15.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4ed42e6696c8dfa5f06728e6441993901f548eb92d73bc472cb5a38d1395fbf", size = 11493299, upload-time = "2026-05-21T14:34:41.836Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c0/56472c251d09858a53e51efbd485b09e1995d8731668b76d52e5dd6ee0f1/ruff-0.15.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:715c543cf450c4888251f91c52f1942a800541d9bddd7ac060aa4e6b77ae7cba", size = 11455931, upload-time = "2026-05-21T14:34:57.276Z" }, + { url = "https://files.pythonhosted.org/packages/2c/4a/e2e7b4d8dbf233d4eace59c75bc3435fa6d8bd3bae82d351d4e4300c0fd1/ruff-0.15.14-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:72ebab6013ec887d439d8b7593737a0a4ffb06d45d209d4e4bf2e92813082d3f", size = 11400794, upload-time = "2026-05-21T14:34:39.773Z" }, + { url = "https://files.pythonhosted.org/packages/97/c7/83c0539fe34c3e09136204d1e75d6052492364e0b3cb05e9465423f567d7/ruff-0.15.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:49072d36abdbe97a8dd7f480afe9c675699c0c495d4c84076e2c1203c4550581", size = 10804759, upload-time = "2026-05-21T14:34:31.045Z" }, + { url = "https://files.pythonhosted.org/packages/86/a6/18f2bfc095a2ab4a78745644e428205532ce6653a5d0fa8501572891534d/ruff-0.15.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:958522aee105068640c2c2ceae08f413ae44d922f52a1374ac13d6a96032fc93", size = 10539517, upload-time = "2026-05-21T14:34:53.064Z" }, + { url = "https://files.pythonhosted.org/packages/54/3a/5a8b3b69c654d4e4bf1d246ac5b49cbcdac6eaab6905925f8915f31e3b80/ruff-0.15.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f3707da619a143a2e8830e2abab8224478d69ace2d28cb6c20543ae97c36bf61", size = 11065169, upload-time = "2026-05-21T14:34:24.484Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c5/8864e4e7925b836ea354b31d57641ec03830564e281a8b6f061f8c3e0ec1/ruff-0.15.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:bb01d645694e3ec0102105d07ef2d53703970407d59c04e59d3ba0b7a1d53553", size = 11560214, upload-time = "2026-05-21T14:34:50.975Z" }, + { url = "https://files.pythonhosted.org/packages/36/38/012bf76752e1f89ed50b77b99532d90f3a3e287bc7918e1fc0948ac866ac/ruff-0.15.14-py3-none-win32.whl", hash = "sha256:6d0c1ad2a0ab718d39b6d8fd2217981ce4d625cd96a720095f798fb47d8b13e6", size = 10805548, upload-time = "2026-05-21T14:34:33.453Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/4ea2c170f10ad760fff2a5250beb18897719dc8b52b53a24cddbb9dd3f19/ruff-0.15.14-py3-none-win_amd64.whl", hash = "sha256:802342981e056db3851a7836e5b070f8f15f67d4a685ae2a6160939d364b2902", size = 11939523, upload-time = "2026-05-21T14:34:18.077Z" }, + { url = "https://files.pythonhosted.org/packages/62/d5/bc97ff895ec35cf3925d4bd60f3b39d822f377a446906ec9bcc87405e59b/ruff-0.15.14-py3-none-win_arm64.whl", hash = "sha256:ff47b90a9ef6a40c9e2f3b479c1fb78531adf055b94c1eba0a7ba04b31951826", size = 11208607, upload-time = "2026-05-21T14:34:26.525Z" }, +] + +[[package]] +name = "s3transfer" +version = "0.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/11/b3/bcdc2f58fa92592db511beda154c2c08d28f21f6c4637f06a42a24b10c21/s3transfer-0.17.1.tar.gz", hash = "sha256:042dd5e3b1b512355e35a23f0223e426b7042e80b97830ea2680ddce327fc45e", size = 159439, upload-time = "2026-05-26T19:45:01.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/dd/904873250a6554fbae40cddbf9198e3cc37a2f1319d5e1a5ce82fe269c17/s3transfer-0.17.1-py3-none-any.whl", hash = "sha256:5b9827d1044159bbb01b86ef8902760ea39281927f5de31de75e1d657177bf4c", size = 88264, upload-time = "2026-05-26T19:45:00.452Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/da/6fbf010c8ebb347679d0d100b22fe9ba5e13fd04046c5df7280d2f0bf706/sqlalchemy-2.0.50.tar.gz", hash = "sha256:af5607d11ef90fd6a5c0549fe0045dce1663d427426bcfb506dcb5346a85a3b9", size = 9907424, upload-time = "2026-05-24T19:20:04.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/b0/a9d19b43f38f878b1278bca5b00b909f7540d41494396dd2561f9ad0956d/sqlalchemy-2.0.50-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae23d8b9d344d30d0a92f06d45825024a5790f1c1dd4cf452636a50d3e58cb", size = 2159807, upload-time = "2026-05-24T19:27:53.086Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/191dd58a248fd2cfd4780fa82c375c505e4ad98c8b522fa69ec492130d77/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47b71b933e7b4ebad407c8fdfd70d2c4f08b78b3238bb30eebdd6eb32ca51b89", size = 3343358, upload-time = "2026-05-24T20:09:29.279Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2b/514fce8a7df81cf5bad7ff7865de7ac0c5776a38cc043475c4703eb7fe8b/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:110fdac56ace278949f00de805edacbd6141e382d992f9ba28238b3a0827a600", size = 3357994, upload-time = "2026-05-24T20:17:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/35/a6/a0e283f5494f92b0d77e319ff77e437b1ffe4a051ba67c81d53234825475/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5e4ac70e9e757f6b3e87c0491ff034442ecd8dfd36d041a50564c322dafc0e", size = 3289399, upload-time = "2026-05-24T20:09:32.239Z" }, + { url = "https://files.pythonhosted.org/packages/b7/96/1b07325ba71752d6a028b77d07bed1483ad545f794e8b1dc89b3ba3b3c68/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:724f3dcbe53dd0151e3cb5e7ec4ba4c620bede579caacd16275dc35ce06e8615", size = 3321216, upload-time = "2026-05-24T20:17:15.581Z" }, + { url = "https://files.pythonhosted.org/packages/ed/8e/bad6ed253e8a99edfc99af02f7173ec48a1d3ed1b9b35a1b8bc1700900cc/sqlalchemy-2.0.50-cp312-cp312-win32.whl", hash = "sha256:1208050441471d003b7c8cb4054fb084f185cf35ac3f0ea270803865bca9939a", size = 2119194, upload-time = "2026-05-24T19:50:04.943Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2d/314a6690dda4b9cfc571eab1a63cf6fe6e1470aa3759ccda6aa016ee0f5a/sqlalchemy-2.0.50-cp312-cp312-win_amd64.whl", hash = "sha256:9d1af51558029a156a70986b7df88f042b3d158d7c8d8fb5072912d4b32d89c7", size = 2146186, upload-time = "2026-05-24T19:50:06.74Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c4/c42356b527296e9862f67990efce31ef78b4cf69cd3f80873a528a060320/sqlalchemy-2.0.50-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:06a9210bdc5f4298cff0781087e2ff45683922252dacc452846373a58761f093", size = 2156697, upload-time = "2026-05-24T19:27:54.764Z" }, + { url = "https://files.pythonhosted.org/packages/60/a1/b1a70e3c4365ac7fe9e347f3710f19b562c866fb96d45e3c891588789a7b/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b53784972ade4f8174b9aa661f31a06f8a936d2cfdd602913ff3c6dd40ae873", size = 3284260, upload-time = "2026-05-24T20:09:34.195Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4a/f3ac3caa19f263d57b0a47f8c91bbf56583dc2d3fc63acfbf644abb24fe0/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31648fa14460537e768a7303b078e4344d208e0d23e06867c1f376a227ed82db", size = 3302280, upload-time = "2026-05-24T20:17:17.825Z" }, + { url = "https://files.pythonhosted.org/packages/66/55/ccada3e3d62254587819749a0bc69f41173eb48a6e385d10e66d32a9c88e/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03f4323c980ad0e918cc9e5369b015f759f4e534db5bbaf4dc36832c10d05064", size = 3231580, upload-time = "2026-05-24T20:09:36.406Z" }, + { url = "https://files.pythonhosted.org/packages/05/f6/6809349130a2de0e109e7f00fd7d431da9565b9b2868b32ee684754f672b/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b9dcc43afef8ac157cd92fce96985d6b8b0cfbd3df4d666f66b4d55a75d202f", size = 3269375, upload-time = "2026-05-24T20:17:20.34Z" }, + { url = "https://files.pythonhosted.org/packages/48/84/278a811ef4e07be9c89dc5cdd7be833268509a66a68c4897cf585e67428f/sqlalchemy-2.0.50-cp313-cp313-win32.whl", hash = "sha256:60922d6599065ddca2c6f376b9aa2f41a6b85a271725e0909490bbc50b1998a5", size = 2117229, upload-time = "2026-05-24T19:50:08.215Z" }, + { url = "https://files.pythonhosted.org/packages/f6/1c/067cc6187ed32d2ec222fe6d2643acc1659a6d0659f8a7cbc5ad3ae83280/sqlalchemy-2.0.50-cp313-cp313-win_amd64.whl", hash = "sha256:287086e67275a212c4582d166a6fb03a65ccc5551d80866270ce0dd9f34eccd3", size = 2143126, upload-time = "2026-05-24T19:50:09.691Z" }, + { url = "https://files.pythonhosted.org/packages/df/32/10ac51b4be7cdecd7e93d069251c86dfbf70b7adbd7c67b48ccea6c49e1c/sqlalchemy-2.0.50-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c966932507a4d7d0a37314927dbfcd89720e3f37d2a1e3352e7ae7939fa8e8a0", size = 2158519, upload-time = "2026-05-24T19:27:56.472Z" }, + { url = "https://files.pythonhosted.org/packages/5a/76/e703d2f7681d7d66c4c891af3f07c7ccf4c76ad7f18351de035b5eda007a/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:faffef4bcc20a1892e65e155293d99d60855bbbc79250ab712819cfd56a8e6bb", size = 3282063, upload-time = "2026-05-24T20:09:38.57Z" }, + { url = "https://files.pythonhosted.org/packages/31/26/ef168b184a25701f9995e8fb7e503fafd7a99c1c77cda1bc1a26ea2ed486/sqlalchemy-2.0.50-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c206aec519a2e7bd08abbfb33436e325fd22c632d9c21a9047e376ce241646e", size = 3287069, upload-time = "2026-05-24T20:17:21.942Z" }, + { url = "https://files.pythonhosted.org/packages/c2/15/765acc2bc693bccc43ca4a95d5b69750da8aaf6db1b5c616536e087f8920/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bef4ac756363227ef6402a75fee025a4bc690f92328e825868939b3b3a446a6d", size = 3230453, upload-time = "2026-05-24T20:09:40.398Z" }, + { url = "https://files.pythonhosted.org/packages/63/61/08e03c3adbf5db0087a0b6816746fec8f3032fb2f7fc899a9bb9b2a48ce4/sqlalchemy-2.0.50-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:96fbee6b19c19cd1556c8bf9419447cf2ec149ffcab7ab64348c23e54ef8547f", size = 3252413, upload-time = "2026-05-24T20:17:24.067Z" }, + { url = "https://files.pythonhosted.org/packages/03/0c/370a1f2db38436c615e10134c8a37de3688e74084792380695f3f5083860/sqlalchemy-2.0.50-cp314-cp314-win32.whl", hash = "sha256:8f00e3eb43ba30eb1b238ee03a8a62309486d1321eda3328bb611e0340033ad8", size = 2120063, upload-time = "2026-05-24T19:50:11.08Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a0/fe92bb9817863bc13ba093bda931979a26cc2ca69f8e8f26d07add3d7c6f/sqlalchemy-2.0.50-cp314-cp314-win_amd64.whl", hash = "sha256:15708c613cd5005b7dffe1f66ee6a63ee8f5e46799f71c70ebad74178c676a39", size = 2145830, upload-time = "2026-05-24T19:50:12.452Z" }, + { url = "https://files.pythonhosted.org/packages/cc/ff/e5640a98a0b2f491eb8fde10fb6c773621a2e44340de231fafcc9370f4a9/sqlalchemy-2.0.50-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3699dac4be410e97049a1658e9480da9cde956594aa0f3aebc60b88f21c5ba70", size = 2178435, upload-time = "2026-05-24T19:42:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/b7/85/337116e186f1236375b5fb70c21cfac98e8e8ab0d3a47be838dc47a59e08/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f96233858e3df43932ac11589e22520da6e8aeb624b03fedfeebb0e8ea213086", size = 3566059, upload-time = "2026-05-24T20:01:20.848Z" }, + { url = "https://files.pythonhosted.org/packages/96/34/bb0e190e161c3c2c24314a65add57218be14a4a9486886b7f5047c1ff7c8/sqlalchemy-2.0.50-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4e70c46fad30c3bcc6a4708bc0130a3173e11a5b25f0ea4a9d8911b450f1f52", size = 3535366, upload-time = "2026-05-24T20:03:56.768Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/a7f759f97e4fd499c5d4e4488c760d5a7fbecf3028b465a04274fcd52384/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1918a3cf564d16d95bca7301005f41ab2ad50b07cd3b9da50d3ed986db148d6a", size = 3474879, upload-time = "2026-05-24T20:01:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d9/2907ea38eb60687d297bf9c39e5ee58053c87b57fe8a9cae97090cecbf10/sqlalchemy-2.0.50-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b00098cdbdbd38c7be3d568b0c9c3122b8c0ec62b911b57cd5e6e0254d60a76d", size = 3486117, upload-time = "2026-05-24T20:03:59.052Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e3/5aa06f167559f8c0bdae487e297d23ba548150ab016a3418265d617a4985/sqlalchemy-2.0.50-cp314-cp314t-win32.whl", hash = "sha256:1fbd55a969d7ac44a98e3dec75016074f809fa08f871585ace58dde110d1bf3e", size = 2150823, upload-time = "2026-05-24T20:08:58.644Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/112fb8f977582d7489d036e409e3723948bcf5320b3ac465f3c481bbe8f9/sqlalchemy-2.0.50-cp314-cp314t-win_amd64.whl", hash = "sha256:c5c3cdb753a9004183e1ccb634b41611654c989e61bc68617ce878e46d6f1e51", size = 2185794, upload-time = "2026-05-24T20:09:00.319Z" }, + { url = "https://files.pythonhosted.org/packages/d0/10/f7220e9b784d295d241c86ed99aeb537f92afcd469a64861f2717e9bb077/sqlalchemy-2.0.50-py3-none-any.whl", hash = "sha256:92064363517a3ff8212b5a93b8c62876579d8dfd1ca5b561335f30152d884fa9", size = 1943861, upload-time = "2026-05-24T19:59:01.119Z" }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + +[[package]] +name = "starlette" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/66/4d20cdf39a8d6a51e663b7038e3b828ff211d3891a43a713fe7e4643f3a8/starlette-1.1.0.tar.gz", hash = "sha256:e83c7fe0ddecd8719c5b840080325aec0260acec86e9832899e377b91d65e90f", size = 2660060, upload-time = "2026-05-23T16:55:41.376Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/79/920b8e0a8b20f793e8d64855095cb8febabf6175b8550b6f7a547d813891/starlette-1.1.0-py3-none-any.whl", hash = "sha256:7f0dfd38e428aad5cb6f9f667f0ca1d2d8ca3f3385dccac8305f79ec98458382", size = 72899, upload-time = "2026-05-23T16:55:39.201Z" }, +] + +[[package]] +name = "stevedore" +version = "5.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/88/35e4d27d9177d7df76d060e0a18f69c6c5794c96960c94042e20a12c8ba2/stevedore-5.8.0.tar.gz", hash = "sha256:b49867b32ca3016e94100e68dbf26e72aa7b8708d0a3f73c08aeb220370ac715", size = 514710, upload-time = "2026-05-18T09:15:27.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/ac/19f9941c74add59d17694930ec8105d5eddeee4ce56dd8632b765ca16d6c/stevedore-5.8.0-py3-none-any.whl", hash = "sha256:88eede9e66ca80e34085b9174e2327da2c61ac91f24f70e41c3ad76e4bb4872b", size = 54553, upload-time = "2026-05-18T09:15:25.82Z" }, +] + +[[package]] +name = "text-unidecode" +version = "1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ab/e2/e9a00f0ccb71718418230718b3d900e71a5d16e701a3dae079a21e9cd8f8/text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93", size = 76885, upload-time = "2019-08-30T21:36:45.405Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "traitlets" +version = "5.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/22/40f55b26baeab80c2d7b3f1db0682f8954e4617fee7d90ce634022ef05c6/traitlets-5.15.0.tar.gz", hash = "sha256:4fead733f81cf1c4c938e06f8ca4633896833c9d89eff878159457f4d4392971", size = 163197, upload-time = "2026-05-06T08:05:58.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/98/a9937a969d018a23badfea0b381f66783649d48e0ea6c41923265c3cbeb3/traitlets-5.15.0-py3-none-any.whl", hash = "sha256:fb36a18867a6803deab09f3c5e0fa81bb7b26a5c9e82501c9933f759166eff40", size = 85877, upload-time = "2026-05-06T08:05:55.853Z" }, +] + +[[package]] +name = "typer" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/1e/a27cc02a0cd715118c71fa2aef2c687fdefc3c28d90fd0dd789c5118154c/typer-0.21.2.tar.gz", hash = "sha256:1abd95a3b675e17ff61b0838ac637fe9478d446d62ad17fa4bb81ea57cc54028", size = 120426, upload-time = "2026-02-10T19:33:46.182Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/cc/d59f893fbdfb5f58770c05febfc4086a46875f1084453621c35605cec946/typer-0.21.2-py3-none-any.whl", hash = "sha256:c3d8de54d00347ef90b82131ca946274f017cffb46683ae3883c360fa958f55c", size = 56728, upload-time = "2026-02-10T19:33:48.01Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "uuid6" +version = "2025.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/b7/4c0f736ca824b3a25b15e8213d1bcfc15f8ac2ae48d1b445b310892dc4da/uuid6-2025.0.1.tar.gz", hash = "sha256:cd0af94fa428675a44e32c5319ec5a3485225ba2179eefcf4c3f205ae30a81bd", size = 13932, upload-time = "2025-07-04T18:30:35.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/b2/93faaab7962e2aa8d6e174afb6f76be2ca0ce89fde14d3af835acebcaa59/uuid6-2025.0.1-py3-none-any.whl", hash = "sha256:80530ce4d02a93cdf82e7122ca0da3ebbbc269790ec1cb902481fa3e9cc9ff99", size = 6979, upload-time = "2025-07-04T18:30:34.001Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.48.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e6/bf/f6544ba992ddb9a6077343a576f9844f7f8f06ab819aefd00206e9255f18/uvicorn-0.48.0.tar.gz", hash = "sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37", size = 91074, upload-time = "2026-05-24T12:08:41.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/be/72532be3da7acc5fdfbccdb95215cd04f995a0886532a5b423f929cda4cc/uvicorn-0.48.0-py3-none-any.whl", hash = "sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad", size = 71410, upload-time = "2026-05-24T12:08:40.258Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "python-discovery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/ba/1f6e8c957e4932be060dcdc482d339c12e0216351478add3645cdaa53c05/virtualenv-21.3.3.tar.gz", hash = "sha256:f5bda277e553b1c2b3c1a8debfc30496e1288cc93ce6b7b71b3280047e317328", size = 7613784, upload-time = "2026-05-13T18:01:30.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/34/a9dbe051de88a63eb7408ea66630bac38e72f7f6077d4be58737106860d9/virtualenv-21.3.3-py3-none-any.whl", hash = "sha256:7d5987d8369e098e41406efb780a3d4ca79280097293899e351a6407ee153ab3", size = 7594554, upload-time = "2026-05-13T18:01:27.815Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/ee/afaf0f85a9a18fe47a67f1e4422ed6cf1fe642f0ae0a2f81166231303c52/wcwidth-0.7.0.tar.gz", hash = "sha256:90e3a7ea092341c44b99562e75d09e4d5160fe7a3974c6fb842a101a95e7eed0", size = 182132, upload-time = "2026-05-02T16:04:12.653Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/52/e465037f5375f43533d1a80b6923955201596a99142ed524d77b571a1418/wcwidth-0.7.0-py3-none-any.whl", hash = "sha256:5d69154c429a82910e241c738cd0e2976fac8a2dd47a1a805f4afed1c0f136f2", size = 110825, upload-time = "2026-05-02T16:04:11.033Z" }, +] + +[[package]] +name = "werkzeug" +version = "3.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, +] + +[[package]] +name = "xmltodict" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/70/80f3b7c10d2630aa66414bf23d210386700aa390547278c789afa994fd7e/xmltodict-1.0.4.tar.gz", hash = "sha256:6d94c9f834dd9e44514162799d344d815a3a4faec913717a9ecbfa5be1bb8e61", size = 26124, upload-time = "2026-02-22T02:21:22.074Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/34/98a2f52245f4d47be93b580dae5f9861ef58977d73a79eb47c58f1ad1f3a/xmltodict-1.0.4-py3-none-any.whl", hash = "sha256:a4a00d300b0e1c59fc2bfccb53d7b2e88c32f200df138a0dd2229f842497026a", size = 13580, upload-time = "2026-02-22T02:21:21.039Z" }, +] + +[[package]] +name = "yaml-config-day" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/ece7b06909afbd673cf2d77a354528174126c22b6f0b57038dc6cf03900d/yaml_config_day-0.0.5.tar.gz", hash = "sha256:88cd1d3aa668c8c2362f2cb36a2ca97cab36c78edc6c673001e0db41235ccf4b", size = 4563, upload-time = "2023-10-07T22:11:56.814Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/5d/c46e00911b71c1a03a3bbef719b081d962e97b0169dd70056c330461cbd9/yaml_config_day-0.0.5-py3-none-any.whl", hash = "sha256:b8aca2c420b9979056257e9ff78b08499d7435cba64baec64b63c806f4fc8ddf", size = 3962, upload-time = "2023-10-07T22:11:54.351Z" }, +] From c343d03f992631d463958da9b567416998a0172a Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 28 May 2026 10:25:53 -0700 Subject: [PATCH 02/47] Formalize inf6 deployment readiness --- AGENTS.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 893ead6..b491752 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,10 @@ +# Shell Session Defaults + +- Default to an interactive shell for shell work. On this Mac, use the user's default shell unless the user explicitly asks for another shell. +- For AWS EC2, ParallelCluster, and other remote Linux hosts, default to an interactive `bash` login shell as `ubuntu`. Do not use `root` unless the user explicitly grants permission for that specific work; use targeted `sudo` from `ubuntu` when escalation is required. +- For Daylily/DayOA/DAY-EC headnode workflow work, use an interactive `ubuntu` tmux/login-shell pane for controllers and workflow commands. Run setup as separate commands in that pane (`source dyoainit`, then `dy-a ...`, then `dy-r ...`) so aliases/functions are defined before use. +- SSM Run Command is for simple inspection or for writing helper scripts through the supported helpers. Do not launch workflow controllers or rely on `dy-*` aliases from non-interactive SSM scripts. + # Bloom CLI Policy ## Session Setup @@ -57,6 +64,16 @@ source ./activate - Service-host certs use DNS-01 renewal; do not depend on HTTP-01 public reachability for Bloom service hosts. - Future dev, test, and stage deployments must use their own approved-source lists, credentials, certificates, TapDB schemas, and tenant data, separate from production. +## Version Tags + +- Use non-v semver tags for package releases, e.g. `2.0.19` or `5.0.21`, not `v2.0.19`. +- Commit first, then tag the exact clean release commit. +- Use annotated tags for release provenance: `git tag -a 2.0.19 -m "Release 2.0.19"`. +- Lightweight tags are acceptable only for scratch/internal marks, not package releases. +- Do not move or overwrite pushed version tags. If a pushed tag is wrong, cut the next patch version. +- If signing is configured and expected, use signed annotated tags: `git tag -s 2.0.19 -m "Release 2.0.19"`. +- Verify tag type with `git cat-file -t 2.0.18`; `tag` means annotated and `commit` means lightweight. + ## Bloom Examples - Start with `source ./activate ` From 901d340d2b8d352e395261613551565ec284a697 Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 28 May 2026 14:14:57 -0700 Subject: [PATCH 03/47] Package Bloom TapDB templates in container image --- Dockerfile | 1 + tests/test_packaging_metadata.py | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 046d925..875e66f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ RUN apt-get update \ COPY pyproject.toml uv.lock README.md ./ RUN uv sync --frozen --no-dev --no-install-project +COPY config ./config COPY bloom_lims ./bloom_lims COPY main.py ./main.py RUN uv sync --frozen --no-dev diff --git a/tests/test_packaging_metadata.py b/tests/test_packaging_metadata.py index ec4b740..0cf2482 100644 --- a/tests/test_packaging_metadata.py +++ b/tests/test_packaging_metadata.py @@ -15,9 +15,17 @@ def test_project_dependencies_pin_release_train_versions() -> None: assert "cli-core-yo==2.1.1" in dependencies assert "daylily-auth-cognito==2.1.5" in dependencies - assert "daylily-tapdb>=7.0.3,<8.0.0" in dependencies + assert "daylily-tapdb>=7.0.5,<8.0.0" in dependencies assert "IPython>=8.18.1" in dependencies assert "psycopg2==2.9.9" in dependencies assert not any(str(dep).startswith("zebra-day") for dep in dependencies) assert "djlint" in dependencies assert "optional-dependencies" not in data["project"] + + +def test_dockerfile_copies_tapdb_template_config() -> None: + dockerfile = (Path(__file__).resolve().parents[1] / "Dockerfile").read_text( + encoding="utf-8" + ) + + assert "COPY config ./config" in dockerfile From c7040467dd939f9d78fe16726b334b59d2abc2f4 Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 28 May 2026 14:46:02 -0700 Subject: [PATCH 04/47] Initialize Bloom container runtime context --- bloom_lims/container_entry.py | 29 +++++++++++++++++- tests/test_container_entry_runtime.py | 42 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/test_container_entry_runtime.py diff --git a/bloom_lims/container_entry.py b/bloom_lims/container_entry.py index 4011afc..657ff4b 100644 --- a/bloom_lims/container_entry.py +++ b/bloom_lims/container_entry.py @@ -5,6 +5,7 @@ import os from pathlib import Path + def _required_env(name: str) -> str: value = os.environ.get(name, "").strip() if not value: @@ -27,8 +28,34 @@ def _start_server(**kwargs: object) -> None: start(**kwargs) +def _initialize_runtime_context(config_path: Path) -> None: + from cli_core_yo.errors import ContextNotInitializedError + from cli_core_yo.runtime import get_context, initialize + from cli_core_yo.xdg import resolve_paths + + from bloom_lims.cli import spec + + try: + get_context() + return + except ContextNotInitializedError: + pass + + initialize( + spec, + resolve_paths(spec.xdg), + config_path=config_path, + invocation={"entrypoint": "container"}, + backend_name="container", + backend_kind="container", + runtime_guard_mode="off", + runtime_check_skipped=True, + ) + + def main() -> None: - _required_absolute_file("BLOOM_CONFIG_PATH") + config_path = _required_absolute_file("BLOOM_CONFIG_PATH") + _initialize_runtime_context(config_path) _start_server( port=int(_required_env("PORT")), host=_required_env("HOST"), diff --git a/tests/test_container_entry_runtime.py b/tests/test_container_entry_runtime.py new file mode 100644 index 0000000..00656b1 --- /dev/null +++ b/tests/test_container_entry_runtime.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from cli_core_yo import runtime as cli_runtime + + +def test_container_entry_initializes_cli_runtime(monkeypatch, tmp_path): + import bloom_lims.container_entry as entry + + config_path = tmp_path / "bloom.yaml" + config_path.write_text("application: {}\n", encoding="utf-8") + monkeypatch.setenv("BLOOM_CONFIG_PATH", str(config_path)) + monkeypatch.setenv("PORT", "8912") + monkeypatch.setenv("HOST", "0.0.0.0") + monkeypatch.setenv("DEPLOYMENT_CODE", "inf9") + monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "xdg" / "config")) + monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "xdg" / "state")) + monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "xdg" / "data")) + monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "xdg" / "cache")) + + observed: dict[str, object] = {} + + def fake_start(**kwargs: object) -> None: + observed["kwargs"] = kwargs + observed["config_path"] = cli_runtime.get_context().config_path + + monkeypatch.setattr(entry, "_start_server", fake_start) + cli_runtime._reset() + try: + entry.main() + finally: + cli_runtime._reset() + + assert observed["config_path"] == config_path + assert observed["kwargs"] == { + "port": 8912, + "host": "0.0.0.0", + "reload": False, + "background": False, + "ssl": False, + "cert": None, + "key": None, + } From fcac343eb5eac33e910e2d20bc70a41c16d65674 Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 28 May 2026 15:09:04 -0700 Subject: [PATCH 05/47] Copy Bloom UI assets into container image --- Dockerfile | 2 ++ tests/test_packaging_metadata.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Dockerfile b/Dockerfile index 875e66f..1aec54f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,8 @@ RUN uv sync --frozen --no-dev --no-install-project COPY config ./config COPY bloom_lims ./bloom_lims COPY main.py ./main.py +COPY static ./static +COPY templates ./templates RUN uv sync --frozen --no-dev FROM python:${PYTHON_VERSION}-slim-bookworm AS runtime diff --git a/tests/test_packaging_metadata.py b/tests/test_packaging_metadata.py index 0cf2482..6213dd8 100644 --- a/tests/test_packaging_metadata.py +++ b/tests/test_packaging_metadata.py @@ -29,3 +29,5 @@ def test_dockerfile_copies_tapdb_template_config() -> None: ) assert "COPY config ./config" in dockerfile + assert "COPY static ./static" in dockerfile + assert "COPY templates ./templates" in dockerfile From e7caca9b9e41e9e49f19d3732553a3659a839e1e Mon Sep 17 00:00:00 2001 From: John Major Date: Fri, 29 May 2026 14:48:01 -0700 Subject: [PATCH 06/47] Standardize Bloom GUI auth entrypoints --- AGENTS.md | 6 ++++++ Dockerfile | 1 + bloom_lims/app.py | 11 +++-------- bloom_lims/gui/routes/auth.py | 4 ++-- bloom_lims/integrations/tapdb_mount.py | 10 +++++++++- pyproject.toml | 2 +- tests/test_deploy_contract.py | 8 ++++++++ tests/test_route_coverage_gaps_gui.py | 5 +---- tests/test_route_inventory_guard.py | 4 ++++ tests/test_tapdb_mount.py | 2 +- 10 files changed, 36 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b491752..74472f0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,3 +81,9 @@ source ./activate - Use `bloom db seed` - Use `bloom server start --port 8912` - Use `tapdb ...` and `daycog ...` only where Bloom docs or Bloom CLI explicitly delegate to them +# DEPLOYED SERVICE GUI AUTH EVIDENCE + +- For deployed service acceptance, API health checks and HTTP `302` redirects are not sufficient. Also use Playwright or the bundled Playwright CLI workflow to open each deployed GUI login page, snapshot the page, click through to the Google OAuth option, and capture screenshots. +- Store screenshots and records under `output/playwright/` or the deployment ledger evidence directory. Record service name, deploy name, URL, timestamp, screenshot paths, and status as `confirmed_success` or `confirmed_fail`. +- If the Google OAuth click reaches an expected external blocker, such as `redirect_uri_mismatch`, record the exact callback URI and mark the row blocked instead of treating the service as healthy. +- Debug and fix failures that are in repository/deployment scope. If the failure requires external configuration or human credentials, annotate the reason precisely. Do not enter real user credentials unless the user explicitly authorizes that in the current turn. diff --git a/Dockerfile b/Dockerfile index 1aec54f..bda0787 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ COPY pyproject.toml uv.lock README.md ./ RUN uv sync --frozen --no-dev --no-install-project COPY config ./config +COPY auth ./auth COPY bloom_lims ./bloom_lims COPY main.py ./main.py COPY static ./static diff --git a/bloom_lims/app.py b/bloom_lims/app.py index d7eb2f0..5fb5d86 100644 --- a/bloom_lims/app.py +++ b/bloom_lims/app.py @@ -193,14 +193,9 @@ async def _enforce_origin_allowlist(request, call_next): app.include_router(probe_router) app.include_router(observability_router) app.include_router(api_v1_router) - try: - from bloom_lims.gui.router import router as gui_router - except ModuleNotFoundError as exc: - logging.warning( - "Skipping GUI router due to missing optional dependency: %s", exc.name - ) - else: - app.include_router(gui_router) + from bloom_lims.gui.router import router as gui_router + + app.include_router(gui_router) register_exception_handlers(app) return app diff --git a/bloom_lims/gui/routes/auth.py b/bloom_lims/gui/routes/auth.py index a93a3ea..2c9165a 100644 --- a/bloom_lims/gui/routes/auth.py +++ b/bloom_lims/gui/routes/auth.py @@ -788,5 +788,5 @@ async def auth_logout_post(request: Request, response: Response): @router.get("/logout") -async def logout(request: Request, response: Response): - return await _logout_response(request, response) +async def logout(): + return RedirectResponse(url="/auth/logout", status_code=status.HTTP_303_SEE_OTHER) diff --git a/bloom_lims/integrations/tapdb_mount.py b/bloom_lims/integrations/tapdb_mount.py index f5a3e02..e44e0b8 100644 --- a/bloom_lims/integrations/tapdb_mount.py +++ b/bloom_lims/integrations/tapdb_mount.py @@ -8,6 +8,7 @@ from dataclasses import dataclass from pathlib import Path from typing import Any, Awaitable, Callable +from urllib.parse import quote from fastapi import FastAPI from starlette.datastructures import Headers @@ -143,7 +144,14 @@ async def __call__( content={"detail": "Authentication required"}, ) else: - response = RedirectResponse(url="/login", status_code=303) + next_path = str(scope.get("path") or "/") + query_string = scope.get("query_string") or b"" + if query_string: + next_path = f"{next_path}?{query_string.decode('utf-8', errors='ignore')}" + response = RedirectResponse( + url=f"/login?next={quote(next_path, safe='/')}", + status_code=303, + ) await response(scope, receive, send) return diff --git a/pyproject.toml b/pyproject.toml index bad3528..65559a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,7 +91,7 @@ version_file = "bloom_lims/_scm_version.py" [tool.setuptools.packages.find] where = ["."] -include = ["bloom_lims*"] +include = ["auth*", "bloom_lims*"] [tool.setuptools.package-data] bloom_lims = ["etc/*.yaml", "etc/*.json"] diff --git a/tests/test_deploy_contract.py b/tests/test_deploy_contract.py index baf5d85..5701efe 100644 --- a/tests/test_deploy_contract.py +++ b/tests/test_deploy_contract.py @@ -43,3 +43,11 @@ def test_db_build_help_mentions_target_option() -> None: result = runner.invoke(build_app(), ["db", "build", "--help"]) assert result.exit_code == 0 assert "--target" in result.output + + +def test_container_packages_gui_auth_module() -> None: + dockerfile = (PROJECT_ROOT / "Dockerfile").read_text(encoding="utf-8") + pyproject = (PROJECT_ROOT / "pyproject.toml").read_text(encoding="utf-8") + + assert "COPY auth ./auth" in dockerfile + assert '"auth*"' in pyproject diff --git a/tests/test_route_coverage_gaps_gui.py b/tests/test_route_coverage_gaps_gui.py index 710123d..7829a83 100644 --- a/tests/test_route_coverage_gaps_gui.py +++ b/tests/test_route_coverage_gaps_gui.py @@ -349,7 +349,4 @@ def test_logout_alias_routes_execute_handler_body(client: TestClient) -> None: logout = client.get("/logout", follow_redirects=False) assert logout.status_code == 303 - assert ( - logout.headers["location"] - == "https://example.auth.us-west-2.amazoncognito.com/logout" - ) + assert logout.headers["location"] == "/auth/logout" diff --git a/tests/test_route_inventory_guard.py b/tests/test_route_inventory_guard.py index f912ea5..8633583 100644 --- a/tests/test_route_inventory_guard.py +++ b/tests/test_route_inventory_guard.py @@ -89,6 +89,10 @@ def _iter_runtime_routes() -> set[tuple[str, str]]: def test_docs_routes_remain_enabled_and_render(client: TestClient) -> None: + root = client.get("/", follow_redirects=False) + assert root.status_code in {200, 302, 303, 307, 308} + assert root.status_code != 404 + openapi = client.get("/openapi.json") assert openapi.status_code == 200 assert openapi.json()["openapi"].startswith("3.") diff --git a/tests/test_tapdb_mount.py b/tests/test_tapdb_mount.py index edd0f36..29ffc7a 100644 --- a/tests/test_tapdb_mount.py +++ b/tests/test_tapdb_mount.py @@ -29,7 +29,7 @@ def test_unauthenticated_request_redirects_to_bloom_login(): with _client() as client: response = client.get("/admin/tapdb/login", follow_redirects=False) assert response.status_code == 303 - assert response.headers.get("location") == "/login" + assert response.headers.get("location") == "/login?next=/admin/tapdb/login" json_response = client.get( "/admin/tapdb/login", From 896569bbec267be573bd566ad1ec5e673c3843b5 Mon Sep 17 00:00:00 2001 From: John Major Date: Mon, 1 Jun 2026 13:10:37 -0700 Subject: [PATCH 07/47] Restore Bloom prefix taxonomy templates --- README.md | 59 + bloom_lims/domain/beta_lab.py | 20 +- bloom_lims/domain/beta_lab_stages.py | 38 +- bloom_lims/domain/beta_lab_store.py | 10 +- bloom_lims/domain/execution_queue.py | 20 +- bloom_lims/etc/prefix_ownership_registry.json | 120 + bloom_lims/schemas/beta_lab.py | 25 +- config/tapdb_templates/bloom/templates.json | 4100 ++++++++++++++++- tests/test_bloom_prefix_taxonomy.py | 162 + tests/test_queue_flow.py | 24 +- tests/test_sequencing_run_contract.py | 48 +- 11 files changed, 4486 insertions(+), 140 deletions(-) create mode 100644 tests/test_bloom_prefix_taxonomy.py diff --git a/README.md b/README.md index 0037720..7b9a562 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,65 @@ curl -k https://localhost:8912/api/v1/external/specimens \ Deep dive: [docs/apis.md](docs/apis.md) +## Prefix Taxonomy + +Bloom EUID prefixes are governed display labels for operators and downstream +records. Prefixes are governance and display labels only; they do not drive +behavior, authorization, routing, template lookup, or workflow dispatch. Code +should use explicit template codes and semantic properties for behavior. + +| Family | Meaning | +| --- | --- | +| `BC*` | Containers | +| `BN*` | Contents, materials, and reagents | +| `BD*` | Data and evidence objects | +| `BR*` | Runs and executions | +| `BG*` | Generic helpers and external-object mappings | + +Concrete template kinds use three-letter prefixes. Two-letter prefixes are +generic class reserves only: `BC`, `BN`, `BD`, `BR`, and `BG`. New Bloom-owned +prefixes must not contain `I`, `L`, `O`, or `U`, and explicit numeric EUID +input must not use leading zeros. + +Current concrete wet-lab prefixes: + +| Template kind | Category | Prefix | +| --- | --- | --- | +| tube container | `container` | `BCT` | +| plate container | `container` | `BCP` | +| well container | `container` | `BCW` | +| bottle container | `container` | `BCB` | +| flowcell container | `container` | `BCF` | +| flowcell lane container | `container` | `BCE` | +| rack, box, or storage container | `container` | `BCR` | +| blood specimen/content | `content` | `BNB` | +| buccal specimen content | `content` | `BNS` | +| saliva specimen content | `content` | `BNA` | +| gDNA content | `content` | `BNG` | +| cfDNA content | `content` | `BNC` | +| sequencing library content | `content` | `BNQ` | +| sequencing library pool content | `content` | `BNP` | +| generic reagent content | `content` | `BNR` | +| sequencing index reagent content | `content` | `BNX` | +| control material content | `content` | `BNK` | +| gDNA quantification data | `data` | `BDQ` | +| extraction run evidence/data | `data` | `BDX` | +| extraction QC run evidence/data | `data` | `BDY` | +| library prep run evidence/data | `data` | `BDP` | +| pooling run evidence/data | `data` | `BDN` | +| library-index assignment data | `data` | `BDA` | +| transfer execution data | `data` | `BDT` | +| Illumina sequencing run | `run` | `BRM` | +| ONT sequencing run | `run` | `BRN` | +| Ultima sequencing run | `run` | `BRT` | +| PacBio sequencing run | `run` | `BRP` | +| Complete Genomics sequencing run | `run` | `BRC` | +| external object mapping | `generic` | `BGX` | + +Invalid historical proposals are explicitly replaced: `BCL` becomes `BCE`, +`BRI` becomes `BRM`, `BRO` becomes `BRN`, `BRU` becomes `BRT`, `BNL` becomes +`BNQ`, and `BDL` becomes `BDP`. Existing historical objects keep their already minted EUIDs; template prefix changes only affect future object creation. + ## GUI Overview Bloom's GUI is an operator and developer surface, not just a demo shell. The main current routes are: diff --git a/bloom_lims/domain/beta_lab.py b/bloom_lims/domain/beta_lab.py index 295d3dd..1c1a4b4 100644 --- a/bloom_lims/domain/beta_lab.py +++ b/bloom_lims/domain/beta_lab.py @@ -35,6 +35,15 @@ class BetaLabService( ORGANIZATION_SITE_REFERENCE_TYPE = "atlas_organization_site" COLLECTION_EVENT_REFERENCE_TYPE = "atlas_collection_event" GENERIC_DATA_TEMPLATE_CODE = "data/generic/generic/1.0" + DATA_TEMPLATE_BY_BETA_KIND = { + "extraction_batch": "data/operation/extraction/1.0", + "extraction_run": "data/operation/extraction/1.0", + "post_extract_qc_result": "data/operation/extraction-qc/1.0", + "library_qc_result": "data/operation/library-prep/1.0", + "pooling_run": "data/operation/pooling/1.0", + "sequenced_library_assignment": "data/library-index-assignment/sequencing-library/1.0", + "run_artifact": "data/execution/transfer/1.0", + } BETA_KIND_QUEUE_DEFINITION = "queue_definition" BETA_KIND_QUEUE_EVENT = "queue_event" BETA_KIND_WORK_ITEM = "beta_work_item" @@ -51,15 +60,18 @@ class BetaLabService( REL_MATERIAL_CONSUMPTION = "beta_material_consumption" REL_USED_INSTRUMENT = "beta_used_instrument" REL_USED_REAGENT = "beta_used_reagent" - POOL_TEMPLATE_CODE = "content/pool/generic/1.0" + POOL_TEMPLATE_CODE = "content/pool/sequencing-library/1.0" POOL_CONTAINER_TEMPLATE_CODE = "container/tube/tube-generic-10ml/1.0" - LIBRARY_PREP_OUTPUT_TEMPLATE_CODE = "data/wetlab/library_prep_output/1.0" + LIBRARY_PREP_OUTPUT_TEMPLATE_CODE = "data/operation/library-prep/1.0" + LIBRARY_MATERIAL_TEMPLATE_CODE = "content/sample/sequencing-library/1.0" SEQUENCING_RUN_TEMPLATE_BY_SUBTYPE = { "illumina": "data/sequencing_run/illumina/1.0", "ont": "data/sequencing_run/ont/1.0", - "novaseq": "data/sequencing_run/novaseq/1.0", + "ultima": "data/sequencing_run/ultima/1.0", + "pacbio": "data/sequencing_run/pacbio/1.0", + "completegenomics": "data/sequencing_run/completegenomics/1.0", } - LIBRARY_PLATE_TEMPLATE_CODE = "container/plate/fixed-plate-96/1.0" + LIBRARY_PLATE_TEMPLATE_CODE = "container/plate/sequencing-library-plate-96/1.0" EXTRACTION_TEMPLATE_BY_TYPE = { "cfdna": "content/sample/cfdna/1.0", "gdna": "content/sample/gdna/1.0", diff --git a/bloom_lims/domain/beta_lab_stages.py b/bloom_lims/domain/beta_lab_stages.py index 9d927fe..ae620b1 100644 --- a/bloom_lims/domain/beta_lab_stages.py +++ b/bloom_lims/domain/beta_lab_stages.py @@ -61,6 +61,7 @@ def _resolve_or_create_stage_data_record( "idempotency_key": record_idempotency_key, "occurred_at": self._timestamp(), }, + template_code=self.DATA_TEMPLATE_BY_BETA_KIND.get(beta_kind), ) def create_extraction( @@ -388,6 +389,9 @@ def record_post_extract_qc( "idempotency_key": idempotency_key or "", "occurred_at": self._timestamp(), }, + template_code=self.DATA_TEMPLATE_BY_BETA_KIND.get( + "post_extract_qc_result" + ), ) if payload.quant_artifact_euid: qc_props = self._props(qc_record) @@ -627,14 +631,13 @@ def create_library_prep( str(self._props(source).get("extraction_type") or "gdna").strip().lower() ) library_material = self.bobj.create_instance_by_code( - self.EXTRACTION_TEMPLATE_BY_TYPE.get( - extraction_type, "content/sample/gdna/1.0" - ), + self.LIBRARY_MATERIAL_TEMPLATE_CODE, { "json_addl": { "properties": { "beta_kind": "library_material", "platform": payload.platform, + "source_extraction_type": extraction_type, "idempotency_key": idempotency_key or "", "metadata": normalized_metadata, } @@ -843,6 +846,7 @@ def record_library_qc( "idempotency_key": idempotency_key or "", "occurred_at": self._timestamp(), }, + template_code=self.DATA_TEMPLATE_BY_BETA_KIND.get("library_qc_result"), ) self.bobj.create_generic_instance_lineage_by_euids( library_material.euid, @@ -980,6 +984,24 @@ def create_pool( pool.name = pool_name pool_props["name"] = pool_name self._write_props(pool, pool_props) + pooling_run = self._create_data_record( + beta_kind="pooling_run", + name=f"pooling:{pool.euid}", + properties={ + "platform": payload.platform, + "member_euids": [member.euid for member in members], + "pool_euid": pool.euid, + "metadata": normalized_metadata, + "idempotency_key": idempotency_key or "", + "occurred_at": self._timestamp(), + }, + template_code=self.DATA_TEMPLATE_BY_BETA_KIND.get("pooling_run"), + ) + self.bobj.create_generic_instance_lineage_by_euids( + pooling_run.euid, + pool.euid, + relationship_type="beta_pooling_run_output", + ) pool_container = self.bobj.create_instance_by_code( payload.pool_container_template_code or self.POOL_CONTAINER_TEMPLATE_CODE, @@ -996,6 +1018,11 @@ def create_pool( pool.euid, relationship_type="beta_pool_member", ) + self.bobj.create_generic_instance_lineage_by_euids( + member.euid, + pooling_run.euid, + relationship_type="beta_pooling_run_input", + ) self._write_graph_metadata( pool, node_role="sequencing_pool", @@ -1065,6 +1092,7 @@ def create_pool( result={ "status": "success", "pool_euid": pool.euid, + "pooling_run_euid": pooling_run.euid, "pool_container_euid": pool_container.euid, "current_queue": self.START_RUN_QUEUE_BY_PLATFORM[payload.platform], "member_count": len(members), @@ -1229,6 +1257,9 @@ def create_run( "library_material_euid": assignment.library_material_euid or "", "barcode_reagent_euid": assignment.barcode_reagent_euid or "", }, + template_code=self.DATA_TEMPLATE_BY_BETA_KIND.get( + "sequenced_library_assignment" + ), ) self.bobj.create_generic_instance_lineage_by_euids( run.euid, @@ -1310,6 +1341,7 @@ def create_run( "s3_uri": s3_uri, "dewey_artifact_euid": dewey_artifact_euid or "", }, + template_code=self.DATA_TEMPLATE_BY_BETA_KIND.get("run_artifact"), ) self.bobj.create_generic_instance_lineage_by_euids( run.euid, diff --git a/bloom_lims/domain/beta_lab_store.py b/bloom_lims/domain/beta_lab_store.py index 91b3bf7..a4381a9 100644 --- a/bloom_lims/domain/beta_lab_store.py +++ b/bloom_lims/domain/beta_lab_store.py @@ -293,8 +293,14 @@ def _ensure_fixed_container_graph_metadata(self, instance) -> None: return type_name = str(instance.type or "").strip() subtype = str(instance.subtype or "").strip() - if type_name == "plate" and subtype in {"fixed-plate-96", "fixed-plate-24"}: - max_wells = 96 if subtype == "fixed-plate-96" else 24 + plate_well_counts = { + "fixed-plate-24": 24, + "fixed-plate-96": 96, + "index-plate-96": 96, + "sequencing-library-plate-96": 96, + } + if type_name == "plate" and subtype in plate_well_counts: + max_wells = plate_well_counts[subtype] self._write_graph_metadata( instance, node_role=subtype, diff --git a/bloom_lims/domain/execution_queue.py b/bloom_lims/domain/execution_queue.py index febf736..7d7e038 100644 --- a/bloom_lims/domain/execution_queue.py +++ b/bloom_lims/domain/execution_queue.py @@ -165,8 +165,7 @@ class ExecutionQueueService: "display_name": "Illumina Library QC", "dispatch_priority": 100, "subject_template_codes": [ - "content/sample/cfdna/1.0", - "content/sample/gdna/1.0", + "content/sample/sequencing-library/1.0", ], "required_worker_capabilities": ["wetlab.library_qc", "platform.ILMN"], }, @@ -183,9 +182,8 @@ class ExecutionQueueService: "display_name": "Illumina Sequencing Pool", "dispatch_priority": 100, "subject_template_codes": [ - "data/wetlab/library_prep_output/1.0", - "content/sample/cfdna/1.0", - "content/sample/gdna/1.0", + "data/operation/library-prep/1.0", + "content/sample/sequencing-library/1.0", ], "required_worker_capabilities": ["wetlab.pooling", "platform.ILMN"], }, @@ -193,22 +191,21 @@ class ExecutionQueueService: "display_name": "ONT Sequencing Pool", "dispatch_priority": 100, "subject_template_codes": [ - "data/wetlab/library_prep_output/1.0", - "content/sample/cfdna/1.0", - "content/sample/gdna/1.0", + "data/operation/library-prep/1.0", + "content/sample/sequencing-library/1.0", ], "required_worker_capabilities": ["wetlab.pooling", "platform.ONT"], }, "ilmn_start_seq_run": { "display_name": "Illumina Start Run", "dispatch_priority": 100, - "subject_template_codes": ["content/pool/generic/1.0"], + "subject_template_codes": ["content/pool/sequencing-library/1.0"], "required_worker_capabilities": ["wetlab.run_start", "platform.ILMN"], }, "ont_start_seq_run": { "display_name": "ONT Start Run", "dispatch_priority": 100, - "subject_template_codes": ["content/pool/generic/1.0"], + "subject_template_codes": ["content/pool/sequencing-library/1.0"], "required_worker_capabilities": ["wetlab.run_start", "platform.ONT"], }, "post_extract_exception": { @@ -224,8 +221,7 @@ class ExecutionQueueService: "display_name": "Illumina Library QC Exception", "dispatch_priority": 10, "subject_template_codes": [ - "content/sample/cfdna/1.0", - "content/sample/gdna/1.0", + "content/sample/sequencing-library/1.0", ], "required_worker_capabilities": [ "wetlab.exception_review", diff --git a/bloom_lims/etc/prefix_ownership_registry.json b/bloom_lims/etc/prefix_ownership_registry.json index 43581fc..f187f31 100644 --- a/bloom_lims/etc/prefix_ownership_registry.json +++ b/bloom_lims/etc/prefix_ownership_registry.json @@ -13,27 +13,147 @@ "BBX": { "issuer_app_code": "bloom" }, + "BC": { + "issuer_app_code": "bloom" + }, + "BCB": { + "issuer_app_code": "bloom" + }, + "BCE": { + "issuer_app_code": "bloom" + }, + "BCF": { + "issuer_app_code": "bloom" + }, "BCN": { "issuer_app_code": "bloom" }, + "BCP": { + "issuer_app_code": "bloom" + }, + "BCR": { + "issuer_app_code": "bloom" + }, "BCT": { "issuer_app_code": "bloom" }, + "BCW": { + "issuer_app_code": "bloom" + }, + "BD": { + "issuer_app_code": "bloom" + }, + "BDA": { + "issuer_app_code": "bloom" + }, + "BDC": { + "issuer_app_code": "bloom" + }, + "BDN": { + "issuer_app_code": "bloom" + }, + "BDP": { + "issuer_app_code": "bloom" + }, + "BDQ": { + "issuer_app_code": "bloom" + }, "BDT": { "issuer_app_code": "bloom" }, + "BDX": { + "issuer_app_code": "bloom" + }, + "BDY": { + "issuer_app_code": "bloom" + }, "BEQ": { "issuer_app_code": "bloom" }, "BFX": { "issuer_app_code": "bloom" }, + "BG": { + "issuer_app_code": "bloom" + }, + "BGM": { + "issuer_app_code": "bloom" + }, + "BGP": { + "issuer_app_code": "bloom" + }, + "BGR": { + "issuer_app_code": "bloom" + }, + "BGS": { + "issuer_app_code": "bloom" + }, "BGT": { "issuer_app_code": "bloom" }, + "BGV": { + "issuer_app_code": "bloom" + }, + "BGX": { + "issuer_app_code": "bloom" + }, "BHE": { "issuer_app_code": "bloom" }, + "BN": { + "issuer_app_code": "bloom" + }, + "BNA": { + "issuer_app_code": "bloom" + }, + "BNB": { + "issuer_app_code": "bloom" + }, + "BNC": { + "issuer_app_code": "bloom" + }, + "BNF": { + "issuer_app_code": "bloom" + }, + "BNG": { + "issuer_app_code": "bloom" + }, + "BNK": { + "issuer_app_code": "bloom" + }, + "BNP": { + "issuer_app_code": "bloom" + }, + "BNQ": { + "issuer_app_code": "bloom" + }, + "BNR": { + "issuer_app_code": "bloom" + }, + "BNS": { + "issuer_app_code": "bloom" + }, + "BNX": { + "issuer_app_code": "bloom" + }, + "BR": { + "issuer_app_code": "bloom" + }, + "BRC": { + "issuer_app_code": "bloom" + }, + "BRM": { + "issuer_app_code": "bloom" + }, + "BRN": { + "issuer_app_code": "bloom" + }, + "BRP": { + "issuer_app_code": "bloom" + }, + "BRT": { + "issuer_app_code": "bloom" + }, "BSJ": { "issuer_app_code": "bloom" }, diff --git a/bloom_lims/schemas/beta_lab.py b/bloom_lims/schemas/beta_lab.py index d6ca59f..ea589d6 100644 --- a/bloom_lims/schemas/beta_lab.py +++ b/bloom_lims/schemas/beta_lab.py @@ -347,8 +347,10 @@ class BetaRunArtifactInput(BaseModel): class BetaRunCreateRequest(BaseModel): pool_euid: str - platform: Literal["ILMN", "ONT"] - run_subtype: Literal["illumina", "ont", "novaseq"] = Field(default="illumina") + platform: Literal["ILMN", "ONT", "Ultima", "PacBio", "CompleteGenomics"] + run_subtype: Literal[ + "illumina", "ont", "ultima", "pacbio", "completegenomics" + ] = Field(default="illumina") flowcell_id: str run_name: str | None = None status: Literal["started", "completed"] = Field(default="completed") @@ -367,13 +369,18 @@ def validate_assignments(self) -> "BetaRunCreateRequest": raise ValueError("flowcell_id must not be empty") if not self.assignments: raise ValueError("assignments must not be empty") - if self.platform == "ONT" and self.run_subtype != "ont": - raise ValueError("platform=ONT requires run_subtype=ont") - if self.platform == "ILMN" and self.run_subtype not in { - "illumina", - "novaseq", - }: - raise ValueError("platform=ILMN requires run_subtype=illumina or novaseq") + expected_subtype_by_platform = { + "ILMN": "illumina", + "ONT": "ont", + "Ultima": "ultima", + "PacBio": "pacbio", + "CompleteGenomics": "completegenomics", + } + expected_subtype = expected_subtype_by_platform[self.platform] + if self.run_subtype != expected_subtype: + raise ValueError( + f"platform={self.platform} requires run_subtype={expected_subtype}" + ) if self.instrument_euid is not None and not self.instrument_euid.strip(): raise ValueError("instrument_euid must not be empty when provided") for field_name in ("operator_start_datetime", "sequencing_end_datetime"): diff --git a/config/tapdb_templates/bloom/templates.json b/config/tapdb_templates/bloom/templates.json index fc7edf2..c6feaaa 100644 --- a/config/tapdb_templates/bloom/templates.json +++ b/config/tapdb_templates/bloom/templates.json @@ -2701,11 +2701,11 @@ { "name": "Generic External Object Link", "polymorphic_discriminator": "generic_template", - "category": "BGT", + "category": "BGX", "type": "generic", "subtype": "external_object_link", "version": "1.0", - "instance_prefix": "BGT", + "instance_prefix": "BGX", "instance_polymorphic_identity": "generic_instance", "bstatus": "active", "is_singleton": false, @@ -2759,11 +2759,11 @@ { "name": "24 fixed well plate", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCP", "type": "plate", "subtype": "fixed-plate-24", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCP", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -3300,11 +3300,11 @@ { "name": "96 well fixed plate", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCP", "type": "plate", "subtype": "fixed-plate-96", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCP", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -4934,11 +4934,11 @@ { "name": "4 position Rack", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCR", "type": "rack", "subtype": "tube-rack-4-empty", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCR", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -4987,11 +4987,11 @@ { "name": "4 position Rack", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCR", "type": "rack", "subtype": "tube-rack-generic", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCR", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -5094,11 +5094,11 @@ { "name": "Shelf", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCR", "type": "shelf", "subtype": "generic", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCR", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -5150,11 +5150,11 @@ { "name": "10ml glass tube", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCT", "type": "tube", "subtype": "tube-10ml-glass", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCT", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -5206,11 +5206,11 @@ { "name": "1.5ml eppi tube", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCT", "type": "tube", "subtype": "tube-eppi-1.5ml", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCT", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -5262,11 +5262,11 @@ { "name": "Generic 10ml tube", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCT", "type": "tube", "subtype": "tube-generic-10ml", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCT", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -5318,11 +5318,11 @@ { "name": "strek 10ml", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCT", "type": "tube", "subtype": "tube-strek-10ml", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCT", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -5375,11 +5375,11 @@ { "name": "Mawi DNA Technologies - ISD-T-250 - iSWAB DNA Collection Tube - Buccal", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCT", "type": "tube", "subtype": "mawi-isd-t-250-iswab-dna-collection-tube-buccal", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCT", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -5435,11 +5435,11 @@ { "name": "plate well - fixed", "polymorphic_discriminator": "container_template", - "category": "BCN", + "category": "BCW", "type": "well", "subtype": "fixed-plate-well", "version": "1.0", - "instance_prefix": "BCN", + "instance_prefix": "BCW", "instance_polymorphic_identity": "container_instance", "bstatus": "active", "is_singleton": false, @@ -5490,11 +5490,11 @@ { "name": "giab NA12878", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNK", "type": "control", "subtype": "giab-HG001", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNK", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5534,11 +5534,11 @@ { "name": "giab NA24385", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNK", "type": "control", "subtype": "giab-HG002", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNK", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5578,11 +5578,11 @@ { "name": "synthetic-cfdna", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNK", "type": "control", "subtype": "synthetic-cfdna", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNK", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5622,11 +5622,11 @@ { "name": "NTC - water", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNK", "type": "control", "subtype": "water-ntc", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNK", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5666,11 +5666,11 @@ { "name": "Pool of Contents", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNP", "type": "pool", "subtype": "generic", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNP", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5702,11 +5702,11 @@ { "name": "Novaseq sequencing kit", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNR", "type": "reagent", "subtype": "ILMN-nova-seq-kit", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNR", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5745,11 +5745,11 @@ { "name": "idt probes for rare mendelian disease, v1.0", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNR", "type": "reagent", "subtype": "idt-probes-rare-mendelian", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNR", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5788,11 +5788,11 @@ { "name": "NaOH", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNR", "type": "reagent", "subtype": "naoh", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNR", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5832,11 +5832,11 @@ { "name": "plasma (whole blood)", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNB", "type": "sample", "subtype": "blood-plasma", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNB", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5870,11 +5870,11 @@ { "name": "cell free DNA ", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNC", "type": "sample", "subtype": "cfdna", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNC", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5908,11 +5908,11 @@ { "name": "genomic DNA", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNG", "type": "sample", "subtype": "gdna", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNG", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5946,11 +5946,11 @@ { "name": "Whole Blood", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNB", "type": "specimen", "subtype": "blood-whole", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNB", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -5984,11 +5984,11 @@ { "name": "Buccal Swab", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNS", "type": "specimen", "subtype": "buccal-swab", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNS", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -6022,11 +6022,11 @@ { "name": "FFPE Block", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNF", "type": "specimen", "subtype": "ffpe-block", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNF", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -6060,11 +6060,11 @@ { "name": "FFPE Slides", "polymorphic_discriminator": "content_template", - "category": "BCT", + "category": "BNF", "type": "specimen", "subtype": "ffpe-slice", "version": "1.0", - "instance_prefix": "BCT", + "instance_prefix": "BNF", "instance_polymorphic_identity": "content_instance", "bstatus": "active", "is_singleton": false, @@ -6335,11 +6335,11 @@ { "name": "Generic Data Record", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "BD", "type": "generic", "subtype": "generic", "version": "1.0", - "instance_prefix": "BDT", + "instance_prefix": "BD", "instance_polymorphic_identity": "data_instance", "bstatus": "active", "is_singleton": false, @@ -6365,11 +6365,11 @@ { "name": "DNA Quant Data", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "BDQ", "type": "generic", "subtype": "gdna-quantification", "version": "1.0", - "instance_prefix": "BDT", + "instance_prefix": "BDQ", "instance_polymorphic_identity": "data_instance", "bstatus": "active", "is_singleton": false, @@ -6408,11 +6408,11 @@ { "name": "Temperature Log Data", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "BDC", "type": "generic", "subtype": "temperature-log", "version": "1.0", - "instance_prefix": "BDT", + "instance_prefix": "BDC", "instance_polymorphic_identity": "data_instance", "bstatus": "active", "is_singleton": false, @@ -6445,11 +6445,11 @@ { "name": "Wet-Lab Library Prep Output", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "BDP", "type": "wetlab", "subtype": "library_prep_output", "version": "1.0", - "instance_prefix": "BDT", + "instance_prefix": "BDP", "instance_polymorphic_identity": "data_instance", "bstatus": "active", "is_singleton": false, @@ -6478,11 +6478,11 @@ { "name": "Illumina Sequencing Run", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "BRM", "type": "sequencing_run", "subtype": "illumina", "version": "1.0", - "instance_prefix": "BDT", + "instance_prefix": "BRM", "instance_polymorphic_identity": "data_instance", "bstatus": "active", "is_singleton": false, @@ -6516,11 +6516,11 @@ { "name": "ONT Sequencing Run", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "BRN", "type": "sequencing_run", "subtype": "ont", "version": "1.0", - "instance_prefix": "BDT", + "instance_prefix": "BRN", "instance_polymorphic_identity": "data_instance", "bstatus": "active", "is_singleton": false, @@ -6552,13 +6552,13 @@ } }, { - "name": "NovaSeq Sequencing Run", + "name": "Ultima Sequencing Run", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "BRT", "type": "sequencing_run", - "subtype": "novaseq", + "subtype": "ultima", "version": "1.0", - "instance_prefix": "BDT", + "instance_prefix": "BRT", "instance_polymorphic_identity": "data_instance", "bstatus": "active", "is_singleton": false, @@ -6569,20 +6569,96 @@ "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", "set_object_status": "BAC/core/set_object_status/1.0" }, - "description": "Sequencing run executed on an Illumina NovaSeq instrument.", + "description": "Sequencing run executed on an Ultima instrument.", "expected_inputs": [], "expected_outputs": [], "instantiation_layouts": [], "properties": { "beta_kind": "sequencing_run", - "platform": "ILMN", - "run_subtype": "novaseq", + "platform": "Ultima", + "run_subtype": "ultima", + "flowcell_id": "", + "operator_start_datetime": null, + "sequencing_end_datetime": null, + "instrument_euid": "", + "metadata": {}, + "name": "Ultima Sequencing Run", + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } + }, + { + "name": "CompleteGenomics Sequencing Run", + "polymorphic_discriminator": "data_template", + "category": "BRC", + "type": "sequencing_run", + "subtype": "completegenomics", + "version": "1.0", + "instance_prefix": "BRC", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Sequencing run executed on a Complete Genomics instrument.", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "beta_kind": "sequencing_run", + "platform": "CompleteGenomics", + "run_subtype": "completegenomics", + "flowcell_id": "", + "operator_start_datetime": null, + "sequencing_end_datetime": null, + "instrument_euid": "", + "metadata": {}, + "name": "CompleteGenomics Sequencing Run", + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } + }, + { + "name": "PacBio Sequencing Run", + "polymorphic_discriminator": "data_template", + "category": "BRP", + "type": "sequencing_run", + "subtype": "pacbio", + "version": "1.0", + "instance_prefix": "BRP", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Sequencing run executed on a PacBio instrument.", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "beta_kind": "sequencing_run", + "platform": "PacBio", + "run_subtype": "pacbio", "flowcell_id": "", "operator_start_datetime": null, "sequencing_end_datetime": null, "instrument_euid": "", "metadata": {}, - "name": "NovaSeq Sequencing Run", + "name": "PacBio Sequencing Run", "idempotency_key": "" }, "singleton": "0", @@ -6630,7 +6706,7 @@ "relationship_type": "contains", "child_templates": [ { - "template_code": "BCN/shelf/generic/1.0", + "template_code": "BCR/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6643,7 +6719,7 @@ } }, { - "template_code": "BCN/shelf/generic/1.0", + "template_code": "BCR/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6712,7 +6788,7 @@ "relationship_type": "contains", "child_templates": [ { - "template_code": "BCN/shelf/generic/1.0", + "template_code": "BCR/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6725,7 +6801,7 @@ } }, { - "template_code": "BCN/shelf/generic/1.0", + "template_code": "BCR/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6738,7 +6814,7 @@ } }, { - "template_code": "BCN/shelf/generic/1.0", + "template_code": "BCR/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6751,7 +6827,7 @@ } }, { - "template_code": "BCN/shelf/generic/1.0", + "template_code": "BCR/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -10382,6 +10458,3870 @@ }, "semantic_category": "workflow_step" } + }, + { + "name": "Sequencing Library Plate 96", + "polymorphic_discriminator": "container_template", + "category": "BCP", + "type": "plate", + "subtype": "sequencing-library-plate-96", + "version": "1.0", + "instance_prefix": "BCP", + "instance_polymorphic_identity": "container_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "download_plate_map": "BAC/plate/download_plate_map/1.0", + "print_barcode_label": "BAC/core/print_barcode_label/1.0", + "set_object_status": "BAC/core/set_object_status/1.0", + "stamp_plates_into_plate": "BAC/plate/stamp_plates_into_plate/1.0" + }, + "cogs": { + "allocation_type": "", + "cost": "4.00", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "state": "active" + }, + "cont_address": { + "col_idx": "0", + "col_name": "", + "row_idx": "0", + "row_name": "" + }, + "description": "96 well fixed plate", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [ + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "1", + "name": "A1", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A1" + } + }, + "name_pattern": "A1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "1", + "name": "A2", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A2" + } + }, + "name_pattern": "A2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "1", + "name": "A3", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A3" + } + }, + "name_pattern": "A3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "1", + "name": "A4", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A4" + } + }, + "name_pattern": "A4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "1", + "name": "A5", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A5" + } + }, + "name_pattern": "A5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "1", + "name": "A6", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A6" + } + }, + "name_pattern": "A6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "1", + "name": "A7", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A7" + } + }, + "name_pattern": "A7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "1", + "name": "A8", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A8" + } + }, + "name_pattern": "A8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "1", + "name": "A9", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A9" + } + }, + "name_pattern": "A9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "1", + "name": "A10", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A10" + } + }, + "name_pattern": "A10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "1", + "name": "A11", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A11" + } + }, + "name_pattern": "A11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "1", + "name": "A12", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A12" + } + }, + "name_pattern": "A12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "2", + "name": "B1", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B1" + } + }, + "name_pattern": "B1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "2", + "name": "B2", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B2" + } + }, + "name_pattern": "B2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "2", + "name": "B3", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B3" + } + }, + "name_pattern": "B3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "2", + "name": "B4", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B4" + } + }, + "name_pattern": "B4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "2", + "name": "B5", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B5" + } + }, + "name_pattern": "B5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "2", + "name": "B6", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B6" + } + }, + "name_pattern": "B6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "2", + "name": "B7", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B7" + } + }, + "name_pattern": "B7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "2", + "name": "B8", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B8" + } + }, + "name_pattern": "B8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "2", + "name": "B9", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B9" + } + }, + "name_pattern": "B9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "2", + "name": "B10", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B10" + } + }, + "name_pattern": "B10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "2", + "name": "B11", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B11" + } + }, + "name_pattern": "B11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "2", + "name": "B12", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B12" + } + }, + "name_pattern": "B12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "3", + "name": "C1", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C1" + } + }, + "name_pattern": "C1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "3", + "name": "C2", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C2" + } + }, + "name_pattern": "C2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "3", + "name": "C3", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C3" + } + }, + "name_pattern": "C3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "3", + "name": "C4", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C4" + } + }, + "name_pattern": "C4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "3", + "name": "C5", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C5" + } + }, + "name_pattern": "C5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "3", + "name": "C6", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C6" + } + }, + "name_pattern": "C6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "3", + "name": "C7", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C7" + } + }, + "name_pattern": "C7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "3", + "name": "C8", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C8" + } + }, + "name_pattern": "C8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "3", + "name": "C9", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C9" + } + }, + "name_pattern": "C9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "3", + "name": "C10", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C10" + } + }, + "name_pattern": "C10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "3", + "name": "C11", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C11" + } + }, + "name_pattern": "C11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "3", + "name": "C12", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C12" + } + }, + "name_pattern": "C12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "4", + "name": "D1", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D1" + } + }, + "name_pattern": "D1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "4", + "name": "D2", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D2" + } + }, + "name_pattern": "D2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "4", + "name": "D3", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D3" + } + }, + "name_pattern": "D3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "4", + "name": "D4", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D4" + } + }, + "name_pattern": "D4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "4", + "name": "D5", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D5" + } + }, + "name_pattern": "D5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "4", + "name": "D6", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D6" + } + }, + "name_pattern": "D6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "4", + "name": "D7", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D7" + } + }, + "name_pattern": "D7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "4", + "name": "D8", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D8" + } + }, + "name_pattern": "D8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "4", + "name": "D9", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D9" + } + }, + "name_pattern": "D9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "4", + "name": "D10", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D10" + } + }, + "name_pattern": "D10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "4", + "name": "D11", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D11" + } + }, + "name_pattern": "D11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "4", + "name": "D12", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D12" + } + }, + "name_pattern": "D12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "5", + "name": "E1", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E1" + } + }, + "name_pattern": "E1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "5", + "name": "E2", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E2" + } + }, + "name_pattern": "E2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "5", + "name": "E3", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E3" + } + }, + "name_pattern": "E3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "5", + "name": "E4", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E4" + } + }, + "name_pattern": "E4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "5", + "name": "E5", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E5" + } + }, + "name_pattern": "E5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "5", + "name": "E6", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E6" + } + }, + "name_pattern": "E6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "5", + "name": "E7", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E7" + } + }, + "name_pattern": "E7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "5", + "name": "E8", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E8" + } + }, + "name_pattern": "E8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "5", + "name": "E9", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E9" + } + }, + "name_pattern": "E9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "5", + "name": "E10", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E10" + } + }, + "name_pattern": "E10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "5", + "name": "E11", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E11" + } + }, + "name_pattern": "E11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "5", + "name": "E12", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E12" + } + }, + "name_pattern": "E12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "6", + "name": "F1", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F1" + } + }, + "name_pattern": "F1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "6", + "name": "F2", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F2" + } + }, + "name_pattern": "F2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "6", + "name": "F3", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F3" + } + }, + "name_pattern": "F3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "6", + "name": "F4", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F4" + } + }, + "name_pattern": "F4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "6", + "name": "F5", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F5" + } + }, + "name_pattern": "F5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "6", + "name": "F6", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F6" + } + }, + "name_pattern": "F6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "6", + "name": "F7", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F7" + } + }, + "name_pattern": "F7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "6", + "name": "F8", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F8" + } + }, + "name_pattern": "F8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "6", + "name": "F9", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F9" + } + }, + "name_pattern": "F9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "6", + "name": "F10", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F10" + } + }, + "name_pattern": "F10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "6", + "name": "F11", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F11" + } + }, + "name_pattern": "F11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "6", + "name": "F12", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F12" + } + }, + "name_pattern": "F12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "7", + "name": "G1", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G1" + } + }, + "name_pattern": "G1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "7", + "name": "G2", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G2" + } + }, + "name_pattern": "G2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "7", + "name": "G3", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G3" + } + }, + "name_pattern": "G3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "7", + "name": "G4", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G4" + } + }, + "name_pattern": "G4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "7", + "name": "G5", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G5" + } + }, + "name_pattern": "G5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "7", + "name": "G6", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G6" + } + }, + "name_pattern": "G6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "7", + "name": "G7", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G7" + } + }, + "name_pattern": "G7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "7", + "name": "G8", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G8" + } + }, + "name_pattern": "G8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "7", + "name": "G9", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G9" + } + }, + "name_pattern": "G9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "7", + "name": "G10", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G10" + } + }, + "name_pattern": "G10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "7", + "name": "G11", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G11" + } + }, + "name_pattern": "G11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "7", + "name": "G12", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G12" + } + }, + "name_pattern": "G12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "8", + "name": "H1", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H1" + } + }, + "name_pattern": "H1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "8", + "name": "H2", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H2" + } + }, + "name_pattern": "H2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "8", + "name": "H3", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H3" + } + }, + "name_pattern": "H3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "8", + "name": "H4", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H4" + } + }, + "name_pattern": "H4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "8", + "name": "H5", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H5" + } + }, + "name_pattern": "H5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "8", + "name": "H6", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H6" + } + }, + "name_pattern": "H6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "8", + "name": "H7", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H7" + } + }, + "name_pattern": "H7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "8", + "name": "H8", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H8" + } + }, + "name_pattern": "H8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "8", + "name": "H9", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H9" + } + }, + "name_pattern": "H9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "8", + "name": "H10", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H10" + } + }, + "name_pattern": "H10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "8", + "name": "H11", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H11" + } + }, + "name_pattern": "H11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "8", + "name": "H12", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H12" + } + }, + "name_pattern": "H12" + } + ] + } + ], + "properties": { + "comments": "", + "lab_code": "", + "lot_expiration_date": "", + "lot_number": "", + "manufacturer": "", + "name": "Sequencing Library Plate 96", + "plate_role": "sequencing_library" + }, + "singleton": "0", + "valid_content": [ + "none" + ], + "valid_parents": [], + "semantic_category": "container" + } + }, + { + "name": "Sequencing Index Plate 96", + "polymorphic_discriminator": "container_template", + "category": "BCP", + "type": "plate", + "subtype": "index-plate-96", + "version": "1.0", + "instance_prefix": "BCP", + "instance_polymorphic_identity": "container_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "download_plate_map": "BAC/plate/download_plate_map/1.0", + "print_barcode_label": "BAC/core/print_barcode_label/1.0", + "set_object_status": "BAC/core/set_object_status/1.0", + "stamp_plates_into_plate": "BAC/plate/stamp_plates_into_plate/1.0" + }, + "cogs": { + "allocation_type": "", + "cost": "4.00", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "state": "active" + }, + "cont_address": { + "col_idx": "0", + "col_name": "", + "row_idx": "0", + "row_name": "" + }, + "description": "96 well fixed plate", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [ + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "1", + "name": "A1", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A1" + } + }, + "name_pattern": "A1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "1", + "name": "A2", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A2" + } + }, + "name_pattern": "A2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "1", + "name": "A3", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A3" + } + }, + "name_pattern": "A3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "1", + "name": "A4", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A4" + } + }, + "name_pattern": "A4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "1", + "name": "A5", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A5" + } + }, + "name_pattern": "A5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "1", + "name": "A6", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A6" + } + }, + "name_pattern": "A6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "1", + "name": "A7", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A7" + } + }, + "name_pattern": "A7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "1", + "name": "A8", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A8" + } + }, + "name_pattern": "A8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "1", + "name": "A9", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A9" + } + }, + "name_pattern": "A9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "1", + "name": "A10", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A10" + } + }, + "name_pattern": "A10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "1", + "name": "A11", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A11" + } + }, + "name_pattern": "A11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "1", + "name": "A12", + "row_idx": "0", + "row_name": "A" + }, + "properties": { + "name": "A12" + } + }, + "name_pattern": "A12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "2", + "name": "B1", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B1" + } + }, + "name_pattern": "B1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "2", + "name": "B2", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B2" + } + }, + "name_pattern": "B2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "2", + "name": "B3", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B3" + } + }, + "name_pattern": "B3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "2", + "name": "B4", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B4" + } + }, + "name_pattern": "B4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "2", + "name": "B5", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B5" + } + }, + "name_pattern": "B5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "2", + "name": "B6", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B6" + } + }, + "name_pattern": "B6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "2", + "name": "B7", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B7" + } + }, + "name_pattern": "B7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "2", + "name": "B8", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B8" + } + }, + "name_pattern": "B8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "2", + "name": "B9", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B9" + } + }, + "name_pattern": "B9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "2", + "name": "B10", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B10" + } + }, + "name_pattern": "B10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "2", + "name": "B11", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B11" + } + }, + "name_pattern": "B11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "2", + "name": "B12", + "row_idx": "1", + "row_name": "B" + }, + "properties": { + "name": "B12" + } + }, + "name_pattern": "B12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "3", + "name": "C1", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C1" + } + }, + "name_pattern": "C1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "3", + "name": "C2", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C2" + } + }, + "name_pattern": "C2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "3", + "name": "C3", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C3" + } + }, + "name_pattern": "C3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "3", + "name": "C4", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C4" + } + }, + "name_pattern": "C4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "3", + "name": "C5", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C5" + } + }, + "name_pattern": "C5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "3", + "name": "C6", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C6" + } + }, + "name_pattern": "C6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "3", + "name": "C7", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C7" + } + }, + "name_pattern": "C7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "3", + "name": "C8", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C8" + } + }, + "name_pattern": "C8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "3", + "name": "C9", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C9" + } + }, + "name_pattern": "C9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "3", + "name": "C10", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C10" + } + }, + "name_pattern": "C10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "3", + "name": "C11", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C11" + } + }, + "name_pattern": "C11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "3", + "name": "C12", + "row_idx": "2", + "row_name": "C" + }, + "properties": { + "name": "C12" + } + }, + "name_pattern": "C12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "4", + "name": "D1", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D1" + } + }, + "name_pattern": "D1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "4", + "name": "D2", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D2" + } + }, + "name_pattern": "D2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "4", + "name": "D3", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D3" + } + }, + "name_pattern": "D3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "4", + "name": "D4", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D4" + } + }, + "name_pattern": "D4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "4", + "name": "D5", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D5" + } + }, + "name_pattern": "D5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "4", + "name": "D6", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D6" + } + }, + "name_pattern": "D6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "4", + "name": "D7", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D7" + } + }, + "name_pattern": "D7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "4", + "name": "D8", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D8" + } + }, + "name_pattern": "D8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "4", + "name": "D9", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D9" + } + }, + "name_pattern": "D9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "4", + "name": "D10", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D10" + } + }, + "name_pattern": "D10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "4", + "name": "D11", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D11" + } + }, + "name_pattern": "D11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "4", + "name": "D12", + "row_idx": "3", + "row_name": "D" + }, + "properties": { + "name": "D12" + } + }, + "name_pattern": "D12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "5", + "name": "E1", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E1" + } + }, + "name_pattern": "E1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "5", + "name": "E2", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E2" + } + }, + "name_pattern": "E2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "5", + "name": "E3", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E3" + } + }, + "name_pattern": "E3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "5", + "name": "E4", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E4" + } + }, + "name_pattern": "E4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "5", + "name": "E5", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E5" + } + }, + "name_pattern": "E5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "5", + "name": "E6", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E6" + } + }, + "name_pattern": "E6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "5", + "name": "E7", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E7" + } + }, + "name_pattern": "E7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "5", + "name": "E8", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E8" + } + }, + "name_pattern": "E8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "5", + "name": "E9", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E9" + } + }, + "name_pattern": "E9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "5", + "name": "E10", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E10" + } + }, + "name_pattern": "E10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "5", + "name": "E11", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E11" + } + }, + "name_pattern": "E11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "5", + "name": "E12", + "row_idx": "4", + "row_name": "E" + }, + "properties": { + "name": "E12" + } + }, + "name_pattern": "E12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "6", + "name": "F1", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F1" + } + }, + "name_pattern": "F1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "6", + "name": "F2", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F2" + } + }, + "name_pattern": "F2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "6", + "name": "F3", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F3" + } + }, + "name_pattern": "F3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "6", + "name": "F4", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F4" + } + }, + "name_pattern": "F4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "6", + "name": "F5", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F5" + } + }, + "name_pattern": "F5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "6", + "name": "F6", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F6" + } + }, + "name_pattern": "F6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "6", + "name": "F7", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F7" + } + }, + "name_pattern": "F7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "6", + "name": "F8", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F8" + } + }, + "name_pattern": "F8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "6", + "name": "F9", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F9" + } + }, + "name_pattern": "F9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "6", + "name": "F10", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F10" + } + }, + "name_pattern": "F10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "6", + "name": "F11", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F11" + } + }, + "name_pattern": "F11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "6", + "name": "F12", + "row_idx": "5", + "row_name": "F" + }, + "properties": { + "name": "F12" + } + }, + "name_pattern": "F12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "7", + "name": "G1", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G1" + } + }, + "name_pattern": "G1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "7", + "name": "G2", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G2" + } + }, + "name_pattern": "G2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "7", + "name": "G3", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G3" + } + }, + "name_pattern": "G3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "7", + "name": "G4", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G4" + } + }, + "name_pattern": "G4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "7", + "name": "G5", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G5" + } + }, + "name_pattern": "G5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "7", + "name": "G6", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G6" + } + }, + "name_pattern": "G6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "7", + "name": "G7", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G7" + } + }, + "name_pattern": "G7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "7", + "name": "G8", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G8" + } + }, + "name_pattern": "G8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "7", + "name": "G9", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G9" + } + }, + "name_pattern": "G9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "7", + "name": "G10", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G10" + } + }, + "name_pattern": "G10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "7", + "name": "G11", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G11" + } + }, + "name_pattern": "G11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "7", + "name": "G12", + "row_idx": "6", + "row_name": "G" + }, + "properties": { + "name": "G12" + } + }, + "name_pattern": "G12" + } + ] + }, + { + "relationship_type": "contains", + "child_templates": [ + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "0", + "col_name": "8", + "name": "H1", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H1" + } + }, + "name_pattern": "H1" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "1", + "col_name": "8", + "name": "H2", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H2" + } + }, + "name_pattern": "H2" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "2", + "col_name": "8", + "name": "H3", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H3" + } + }, + "name_pattern": "H3" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "3", + "col_name": "8", + "name": "H4", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H4" + } + }, + "name_pattern": "H4" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "4", + "col_name": "8", + "name": "H5", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H5" + } + }, + "name_pattern": "H5" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "5", + "col_name": "8", + "name": "H6", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H6" + } + }, + "name_pattern": "H6" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "6", + "col_name": "8", + "name": "H7", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H7" + } + }, + "name_pattern": "H7" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "7", + "col_name": "8", + "name": "H8", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H8" + } + }, + "name_pattern": "H8" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "8", + "col_name": "8", + "name": "H9", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H9" + } + }, + "name_pattern": "H9" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "9", + "col_name": "8", + "name": "H10", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H10" + } + }, + "name_pattern": "H10" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "10", + "col_name": "8", + "name": "H11", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H11" + } + }, + "name_pattern": "H11" + }, + { + "template_code": "container/well/fixed-plate-well/*/", + "json_addl": { + "cont_address": { + "col_idx": "11", + "col_name": "8", + "name": "H12", + "row_idx": "7", + "row_name": "H" + }, + "properties": { + "name": "H12" + } + }, + "name_pattern": "H12" + } + ] + } + ], + "properties": { + "comments": "", + "lab_code": "", + "lot_expiration_date": "", + "lot_number": "", + "manufacturer": "", + "name": "Sequencing Index Plate 96", + "plate_role": "sequencing_index" + }, + "singleton": "0", + "valid_content": [ + "none" + ], + "valid_parents": [], + "semantic_category": "container" + } + }, + { + "name": "Generic Reagent Bottle", + "polymorphic_discriminator": "container_template", + "category": "BCB", + "type": "bottle", + "subtype": "generic", + "version": "1.0", + "instance_prefix": "BCB", + "instance_polymorphic_identity": "container_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "print_barcode_label": "BAC/core/print_barcode_label/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "cogs": { + "allocation_type": "", + "cost": "1.50", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "state": "active" + }, + "cont_address": { + "col_idx": "0", + "col_name": "", + "name": "", + "row_idx": "0", + "row_name": "" + }, + "description": "Generic 10ml tube", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "comments": "", + "lab_code": "", + "lot_expiration_date": "", + "lot_number": "", + "manufacturer": "", + "manufacturer_other": "", + "name": "Generic Reagent Bottle", + "container_role": "bottle" + }, + "valid_content": [ + "content/.*" + ], + "valid_parents": [], + "semantic_category": "container" + } + }, + { + "name": "Flowcell", + "polymorphic_discriminator": "container_template", + "category": "BCF", + "type": "flowcell", + "subtype": "generic", + "version": "1.0", + "instance_prefix": "BCF", + "instance_polymorphic_identity": "container_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "print_barcode_label": "BAC/core/print_barcode_label/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "cogs": { + "allocation_type": "", + "cost": "1.50", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "state": "active" + }, + "cont_address": { + "col_idx": "0", + "col_name": "", + "name": "", + "row_idx": "0", + "row_name": "" + }, + "description": "Generic 10ml tube", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "Flowcell", + "platform": "", + "flowcell_id": "", + "manufacturer": "", + "lot_number": "", + "comments": "", + "lab_code": "" + }, + "valid_content": [ + "content/.*" + ], + "valid_parents": [], + "semantic_category": "container" + } + }, + { + "name": "Flowcell Lane", + "polymorphic_discriminator": "container_template", + "category": "BCE", + "type": "flowcell_lane", + "subtype": "generic", + "version": "1.0", + "instance_prefix": "BCE", + "instance_polymorphic_identity": "container_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "cogs": { + "allocation_type": "", + "cost": "0.00", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "state": "active" + }, + "cont_address": { + "col_idx": "0", + "col_name": "", + "row_idx": "0", + "row_name": "" + }, + "description": "plate well - fixed", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "Flowcell Lane", + "flowcell_euid": "", + "lane": "", + "lane_index": "", + "comments": "", + "lab_code": "" + }, + "singleton": "0", + "valid_content": [ + "content/.*" + ], + "valid_parents": [ + "container/plate/fixed-plate.*?/.*?" + ], + "semantic_category": "container" + } + }, + { + "name": "Saliva Specimen", + "polymorphic_discriminator": "content_template", + "category": "BNA", + "type": "specimen", + "subtype": "saliva", + "version": "1.0", + "instance_prefix": "BNA", + "instance_polymorphic_identity": "content_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "description": "Buccal Swab", + "instantiation_layouts": [], + "cogs": { + "state": "active", + "cost": "0.00", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "allocation_type": "" + }, + "properties": { + "name": "Saliva Specimen", + "volume": "", + "volume_units": "", + "comments": "", + "lab_code": "" + }, + "expected_inputs": [], + "singleton": "0", + "expected_outputs": [], + "actions": {}, + "semantic_category": "content" + } + }, + { + "name": "Sequencing Library Content", + "polymorphic_discriminator": "content_template", + "category": "BNQ", + "type": "sample", + "subtype": "sequencing-library", + "version": "1.0", + "instance_prefix": "BNQ", + "instance_polymorphic_identity": "content_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "description": "genomic DNA", + "properties": { + "name": "Sequencing Library Content", + "library_type": "", + "platform": "", + "volume": "", + "volume_units": "", + "comments": "", + "lab_code": "" + }, + "expected_inputs": [], + "singleton": "0", + "expected_outputs": [], + "actions": {}, + "instantiation_layouts": [], + "cogs": { + "state": "active", + "cost": "0.00", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "allocation_type": "" + }, + "semantic_category": "content" + } + }, + { + "name": "Sequencing Library Pool Content", + "polymorphic_discriminator": "content_template", + "category": "BNP", + "type": "pool", + "subtype": "sequencing-library", + "version": "1.0", + "instance_prefix": "BNP", + "instance_polymorphic_identity": "content_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "description": "Pool of Contents", + "properties": { + "name": "Sequencing Library Pool Content", + "platform": "", + "pool_strategy": "", + "member_count": 0, + "comments": "", + "lab_code": "" + }, + "expected_inputs": [], + "expected_outputs": [], + "singleton": "0", + "actions": {}, + "instantiation_layouts": [], + "cogs": { + "state": "active", + "cost": "0.00", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "allocation_type": "" + }, + "semantic_category": "content" + } + }, + { + "name": "Sequencing Index Reagent Content", + "polymorphic_discriminator": "content_template", + "category": "BNX", + "type": "reagent", + "subtype": "sequencing-index", + "version": "1.0", + "instance_prefix": "BNX", + "instance_polymorphic_identity": "content_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "description": "Novaseq sequencing kit", + "singleton": "0", + "properties": { + "name": "Sequencing Index Reagent Content", + "index_name": "", + "index_sequence": "", + "index_set": "", + "well_name": "", + "plate_euid": "", + "volume": "", + "volume_units": "", + "comments": "", + "manufacturer": "", + "lot_number": "", + "expiration_date": "", + "received_date": "", + "storage_conditions": "", + "qualification_status": "", + "lab_code": "" + }, + "expected_inputs": [], + "expected_outputs": [], + "actions": {}, + "instantiation_layouts": [], + "cogs": { + "state": "active", + "cost": "1.00", + "cost_split_by_children": [ + { + "*/*/*/*": {} + } + ], + "allocation_type": "" + }, + "semantic_category": "content" + } + }, + { + "name": "gDNA Quantification Data", + "polymorphic_discriminator": "data_template", + "category": "BDQ", + "type": "quantification", + "subtype": "gdna", + "version": "1.0", + "instance_prefix": "BDQ", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Generic data record", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "gDNA Quantification Data", + "beta_kind": "gdna_quantification", + "target_euid": "", + "well_euid": "", + "well_name": "", + "method": "", + "concentration": null, + "concentration_units": "", + "total_yield": null, + "yield_units": "", + "passed": null, + "metadata": {}, + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } + }, + { + "name": "Extraction Run Evidence", + "polymorphic_discriminator": "data_template", + "category": "BDX", + "type": "operation", + "subtype": "extraction", + "version": "1.0", + "instance_prefix": "BDX", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Generic data record", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "Extraction Run Evidence", + "beta_kind": "extraction_run", + "operation_type": "extraction", + "source_specimen_euid": "", + "plate_euid": "", + "started_at": "", + "completed_at": "", + "reagent_euids": [], + "metadata": {}, + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } + }, + { + "name": "Extraction QC Run Evidence", + "polymorphic_discriminator": "data_template", + "category": "BDY", + "type": "operation", + "subtype": "extraction-qc", + "version": "1.0", + "instance_prefix": "BDY", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Generic data record", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "Extraction QC Run Evidence", + "beta_kind": "post_extract_qc_result", + "operation_type": "extraction_qc", + "extraction_output_euid": "", + "metrics": {}, + "passed": null, + "metadata": {}, + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } + }, + { + "name": "Library Prep Run Evidence", + "polymorphic_discriminator": "data_template", + "category": "BDP", + "type": "operation", + "subtype": "library-prep", + "version": "1.0", + "instance_prefix": "BDP", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Generic data record", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "Library Prep Run Evidence", + "beta_kind": "library_prep_output", + "operation_type": "library_prep", + "platform": "", + "source_extraction_output_euid": "", + "reagent_euids": [], + "metadata": {}, + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } + }, + { + "name": "Pooling Run Evidence", + "polymorphic_discriminator": "data_template", + "category": "BDN", + "type": "operation", + "subtype": "pooling", + "version": "1.0", + "instance_prefix": "BDN", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Generic data record", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "Pooling Run Evidence", + "beta_kind": "pooling_run", + "operation_type": "pooling", + "platform": "", + "member_euids": [], + "pool_euid": "", + "metadata": {}, + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } + }, + { + "name": "Library Index Assignment Data", + "polymorphic_discriminator": "data_template", + "category": "BDA", + "type": "library-index-assignment", + "subtype": "sequencing-library", + "version": "1.0", + "instance_prefix": "BDA", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Generic data record", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "Library Index Assignment Data", + "beta_kind": "sequenced_library_assignment", + "flowcell_id": "", + "lane": "", + "library_barcode": "", + "library_prep_output_euid": "", + "library_material_euid": "", + "barcode_reagent_euid": "", + "metadata": {}, + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } + }, + { + "name": "Transfer Execution Data", + "polymorphic_discriminator": "data_template", + "category": "BDT", + "type": "execution", + "subtype": "transfer", + "version": "1.0", + "instance_prefix": "BDT", + "instance_polymorphic_identity": "data_instance", + "bstatus": "active", + "is_singleton": false, + "json_addl": { + "action_groups": {}, + "action_imports": { + "add_relationships": "BAC/core/add-relationships/1.0", + "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", + "set_object_status": "BAC/core/set_object_status/1.0" + }, + "description": "Generic data record", + "expected_inputs": [], + "expected_outputs": [], + "instantiation_layouts": [], + "properties": { + "name": "Transfer Execution Data", + "beta_kind": "transfer_execution", + "source_euid": "", + "destination_euid": "", + "transfer_type": "", + "status": "", + "metadata": {}, + "idempotency_key": "" + }, + "singleton": "0", + "semantic_category": "data" + } } ] } diff --git a/tests/test_bloom_prefix_taxonomy.py b/tests/test_bloom_prefix_taxonomy.py new file mode 100644 index 0000000..119c60a --- /dev/null +++ b/tests/test_bloom_prefix_taxonomy.py @@ -0,0 +1,162 @@ +"""Bloom prefix taxonomy and wet-lab template contract tests.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import pytest + +from bloom_lims.core.validation import ValidationError, validate_euid + + +PROJECT_ROOT = Path(__file__).resolve().parents[1] +FORBIDDEN_PREFIX_CHARS = set("ILOU") + + +def _template_pack() -> dict: + return json.loads( + ( + PROJECT_ROOT / "config" / "tapdb_templates" / "bloom" / "templates.json" + ).read_text(encoding="utf-8") + ) + + +def _templates_by_code() -> dict[str, dict]: + templates: dict[str, dict] = {} + for template in _template_pack()["templates"]: + semantic_category = template["json_addl"].get("semantic_category") + code = ( + f"{semantic_category}/{template['type']}/" + f"{template['subtype']}/{template['version']}" + ) + templates[code] = template + return templates + + +def test_wet_lab_templates_use_bloom_prefix_taxonomy() -> None: + templates = _templates_by_code() + + expected_prefixes = { + "container/tube/tube-generic-10ml/1.0": "BCT", + "container/plate/fixed-plate-96/1.0": "BCP", + "container/plate/sequencing-library-plate-96/1.0": "BCP", + "container/plate/index-plate-96/1.0": "BCP", + "container/well/fixed-plate-well/1.0": "BCW", + "container/bottle/generic/1.0": "BCB", + "container/flowcell/generic/1.0": "BCF", + "container/flowcell_lane/generic/1.0": "BCE", + "content/specimen/blood-whole/1.0": "BNB", + "content/specimen/buccal-swab/1.0": "BNS", + "content/specimen/saliva/1.0": "BNA", + "content/sample/gdna/1.0": "BNG", + "content/sample/cfdna/1.0": "BNC", + "content/sample/sequencing-library/1.0": "BNQ", + "content/pool/sequencing-library/1.0": "BNP", + "content/reagent/sequencing-index/1.0": "BNX", + "data/generic/gdna-quantification/1.0": "BDQ", + "data/quantification/gdna/1.0": "BDQ", + "data/operation/extraction/1.0": "BDX", + "data/operation/extraction-qc/1.0": "BDY", + "data/operation/library-prep/1.0": "BDP", + "data/operation/pooling/1.0": "BDN", + "data/library-index-assignment/sequencing-library/1.0": "BDA", + "data/execution/transfer/1.0": "BDT", + "generic/generic/external_object_link/1.0": "BGX", + } + + for code, expected_prefix in expected_prefixes.items(): + template = templates[code] + assert template["category"] == expected_prefix + assert template["instance_prefix"] == expected_prefix + + +def test_prefix_taxonomy_rejects_forbidden_letters_and_leading_zero_examples() -> None: + prefixes = { + template["instance_prefix"] + for template in _template_pack()["templates"] + if template.get("instance_prefix") + } + registry = json.loads( + ( + PROJECT_ROOT / "bloom_lims" / "etc" / "prefix_ownership_registry.json" + ).read_text(encoding="utf-8") + ) + prefixes.update(registry["ownership"]["Z"]) + + assert all(not (set(prefix) & FORBIDDEN_PREFIX_CHARS) for prefix in prefixes) + for bad_prefix in ("BCL", "BRI", "BRO", "BRU", "BNL", "BDL"): + assert bad_prefix not in prefixes + + assert validate_euid("BCP1") + assert validate_euid("BD1") + with pytest.raises(ValidationError, match="No leading zeros"): + validate_euid("BCP01") + + +def test_prefixes_are_declared_in_packaged_ownership_registry() -> None: + registry = json.loads( + ( + PROJECT_ROOT / "bloom_lims" / "etc" / "prefix_ownership_registry.json" + ).read_text(encoding="utf-8") + ) + claims = registry["ownership"]["Z"] + + for prefix in { + "BC", + "BCT", + "BCP", + "BCW", + "BCB", + "BCF", + "BCE", + "BCR", + "BN", + "BNB", + "BNS", + "BNA", + "BNG", + "BNC", + "BNQ", + "BNP", + "BNR", + "BNX", + "BNK", + "BD", + "BDQ", + "BDX", + "BDY", + "BDP", + "BDN", + "BDA", + "BDT", + "BR", + "BRM", + "BRN", + "BRT", + "BRP", + "BRC", + "BG", + "BGX", + "BGM", + "BGP", + "BGV", + "BGR", + "BGS", + }: + assert claims[prefix]["issuer_app_code"] == "bloom" + + +def test_readme_documents_taxonomy_and_historical_object_rule() -> None: + readme = (PROJECT_ROOT / "README.md").read_text(encoding="utf-8") + + for snippet in ( + "BC*` | Containers", + "BN*` | Contents, materials, and reagents", + "BD*` | Data and evidence objects", + "BR*` | Runs and executions", + "BG*` | Generic helpers and external-object mappings", + "Existing historical objects keep their already minted EUIDs", + "Prefixes are governance and display labels only", + ): + assert snippet in readme diff --git a/tests/test_queue_flow.py b/tests/test_queue_flow.py index 071959f..c5e508b 100644 --- a/tests/test_queue_flow.py +++ b/tests/test_queue_flow.py @@ -249,8 +249,8 @@ def test_beta_queue_flow_end_to_end(): extraction_body["atlas_test_fulfillment_item_euid"] == atlas_context["fulfillment_items"][0]["atlas_test_fulfillment_item_euid"] ) - _assert_domain_scoped_euid(extraction_body["extraction_batch_euid"], "BDT-") - _assert_domain_scoped_euid(extraction_body["extraction_run_euid"], "BDT-") + _assert_domain_scoped_euid(extraction_body["extraction_batch_euid"], "BDX-") + _assert_domain_scoped_euid(extraction_body["extraction_run_euid"], "BDX-") extraction_output_euid = extraction_body["extraction_output_euid"] extraction_batch_euid = extraction_body["extraction_batch_euid"] extraction_run_euid = extraction_body["extraction_run_euid"] @@ -281,7 +281,7 @@ def test_beta_queue_flow_end_to_end(): qc_body = qc.json() assert qc_body["current_queue"] == "ilmn_lib_prep" assert qc_body["next_queue"] == "ilmn_lib_prep" - _assert_domain_scoped_euid(qc_body["qc_record_euid"], "BDT-") + _assert_domain_scoped_euid(qc_body["qc_record_euid"], "BDY-") ont_library_prep = client.post( "/api/v1/external/atlas/beta/library-prep", @@ -316,10 +316,10 @@ def test_beta_queue_flow_end_to_end(): library_container_euid = library_body["library_container_euid"] library_plate_euid = library_body["library_plate_euid"] library_well_euid = library_body["library_well_euid"] - _assert_domain_scoped_euid(library_material_euid, "BCT-") - _assert_domain_scoped_euid(library_container_euid, "BCN-") - _assert_domain_scoped_euid(library_plate_euid, "BCN-") - _assert_domain_scoped_euid(library_well_euid, "BCN-") + _assert_domain_scoped_euid(library_material_euid, "BNQ-") + _assert_domain_scoped_euid(library_container_euid, "BCP-") + _assert_domain_scoped_euid(library_plate_euid, "BCP-") + _assert_domain_scoped_euid(library_well_euid, "BCW-") library_qc = client.post( "/api/v1/external/atlas/beta/library-qc", @@ -337,7 +337,7 @@ def test_beta_queue_flow_end_to_end(): assert library_qc_body["qc_passed"] is True assert library_qc_body["next_queue"] == "ilmn_seq_pool" assert library_qc_body["current_queue"] == "ilmn_seq_pool" - _assert_domain_scoped_euid(library_qc_body["qc_record_euid"], "BDT-") + _assert_domain_scoped_euid(library_qc_body["qc_record_euid"], "BDP-") pool = client.post( "/api/v1/external/atlas/beta/pools", @@ -367,7 +367,7 @@ def test_beta_queue_flow_end_to_end(): json={ "pool_euid": pool_euid, "platform": "ILMN", - "run_subtype": "novaseq", + "run_subtype": "illumina", "flowcell_id": flowcell_id, "run_name": "beta-ilmn-run", "status": "completed", @@ -402,7 +402,7 @@ def test_beta_queue_flow_end_to_end(): assert run_body["artifact_count"] == 1 assert run_body["assignment_count"] == 1 assert run_body["flowcell_id"] == flowcell_id - assert run_body["run_subtype"] == "novaseq" + assert run_body["run_subtype"] == "illumina" assert run_body["operator_start_datetime"] == "2026-05-20T06:00:00Z" assert run_body["sequencing_end_datetime"] == "2026-05-20T14:30:00Z" assert run_body["instrument_euid"] == instrument_euid @@ -421,9 +421,9 @@ def test_beta_queue_flow_end_to_end(): ) run_props = (run_instance.json_addl or {}).get("properties", {}) assert run_instance.type == "sequencing_run" - assert run_instance.subtype == "novaseq" + assert run_instance.subtype == "illumina" assert run_props["beta_kind"] == "sequencing_run" - assert run_props["run_subtype"] == "novaseq" + assert run_props["run_subtype"] == "illumina" assert run_props["operator_start_datetime"] == "2026-05-20T06:00:00+00:00" assert run_props["sequencing_end_datetime"] == "2026-05-20T14:30:00+00:00" assert run_props["instrument_euid"] == instrument_euid diff --git a/tests/test_sequencing_run_contract.py b/tests/test_sequencing_run_contract.py index 14c04b4..cb6d1c2 100644 --- a/tests/test_sequencing_run_contract.py +++ b/tests/test_sequencing_run_contract.py @@ -16,7 +16,7 @@ def _minimal_run_payload(**overrides): payload = { - "pool_euid": "BCT-POOL-1", + "pool_euid": "BNP-POOL-1", "platform": "ILMN", "run_subtype": "illumina", "flowcell_id": "FLOW-1", @@ -26,7 +26,7 @@ def _minimal_run_payload(**overrides): { "lane": "1", "library_barcode": "IDX-1", - "library_prep_output_euid": "BDT-LIB-1", + "library_prep_output_euid": "BDP-LIB-1", } ], } @@ -51,15 +51,21 @@ def test_sequencing_run_templates_are_shipped_for_tapdb_seed(): } expected = { - ("data", "sequencing_run", "illumina", "1.0"): "ILMN", - ("data", "sequencing_run", "ont", "1.0"): "ONT", - ("data", "sequencing_run", "novaseq", "1.0"): "ILMN", + ("data", "sequencing_run", "illumina", "1.0"): ("ILMN", "BRM"), + ("data", "sequencing_run", "ont", "1.0"): ("ONT", "BRN"), + ("data", "sequencing_run", "ultima", "1.0"): ("Ultima", "BRT"), + ("data", "sequencing_run", "completegenomics", "1.0"): ( + "CompleteGenomics", + "BRC", + ), + ("data", "sequencing_run", "pacbio", "1.0"): ("PacBio", "BRP"), } - for template_key, platform in expected.items(): + for template_key, (platform, instance_prefix) in expected.items(): template = templates[template_key] props = template["json_addl"]["properties"] - assert template["category"] == "BDT" - assert template["instance_prefix"] == "BDT" + assert template["category"] == instance_prefix + assert template["instance_prefix"] == instance_prefix + assert template["json_addl"]["semantic_category"] == "data" assert props["beta_kind"] == "sequencing_run" assert props["platform"] == platform assert props["run_subtype"] == template_key[2] @@ -69,18 +75,24 @@ def test_sequencing_run_templates_are_shipped_for_tapdb_seed(): def test_run_schema_validates_subtype_platform_and_datetime_contract(): - novaseq = BetaRunCreateRequest.model_validate( - _minimal_run_payload(run_subtype="novaseq") - ) - assert novaseq.run_subtype == "novaseq" - - ont = BetaRunCreateRequest.model_validate( - _minimal_run_payload(platform="ONT", run_subtype="ont") - ) - assert ont.platform == "ONT" + expected = { + "ILMN": "illumina", + "ONT": "ont", + "Ultima": "ultima", + "PacBio": "pacbio", + "CompleteGenomics": "completegenomics", + } + for platform, run_subtype in expected.items(): + run = BetaRunCreateRequest.model_validate( + _minimal_run_payload(platform=platform, run_subtype=run_subtype) + ) + assert run.platform == platform + assert run.run_subtype == run_subtype with pytest.raises(ValidationError, match="platform=ONT requires run_subtype=ont"): - BetaRunCreateRequest.model_validate(_minimal_run_payload(platform="ONT")) + BetaRunCreateRequest.model_validate( + _minimal_run_payload(platform="ONT", run_subtype="illumina") + ) with pytest.raises(ValidationError, match="must include a timezone"): BetaRunCreateRequest.model_validate( From 3bfa5b5f7f4078cf25d92b12fbfbd29e31c27dcf Mon Sep 17 00:00:00 2001 From: John Major Date: Tue, 2 Jun 2026 00:43:03 -0700 Subject: [PATCH 08/47] Repair Bloom EUID actions and retire queue GUI --- bloom_lims/config/action/core.json | 58 +++++++++++++++++++-- bloom_lims/gui/actions.py | 39 +++++++++++++- bloom_lims/gui/routes/operations.py | 23 ++++++-- templates/modern/base.html | 3 -- templates/modern/dashboard.html | 42 --------------- templates/modern/euid_details.html | 44 +++++++--------- templates/modern/queue_retired.html | 28 ++++++++++ tests/test_action_button_template_safety.py | 7 +++ tests/test_action_schema_hydration.py | 54 +++++++++++++++++++ tests/test_gui_endpoints.py | 17 +++--- 10 files changed, 229 insertions(+), 86 deletions(-) create mode 100644 templates/modern/queue_retired.html diff --git a/bloom_lims/config/action/core.json b/bloom_lims/config/action/core.json index 5951321..dff7c65 100644 --- a/bloom_lims/config/action/core.json +++ b/bloom_lims/config/action/core.json @@ -47,7 +47,7 @@ "label_style": "" }, "curr_user": "", - "description": "Print Label2", + "description": "Print Label", "ui_schema": { "title": "Print Label", "fields": [] @@ -78,7 +78,58 @@ "description": "Add Parent or Child Relationships (aka lineages).", "ui_schema": { "title": "Add Parent or Child Relationship(s)", - "fields": [] + "fields": [ + { + "name": "lineage_type_to_create", + "label": "Create Relationship As", + "type": "select", + "required": true, + "options": [ + {"value": "parent", "label": "parent"}, + {"value": "child", "label": "child"} + ] + }, + { + "name": "relationship_type", + "label": "Relationship Type", + "type": "select", + "required": true, + "options": [ + {"value": "contains", "label": "contains"}, + {"value": "derived_from", "label": "derived_from"}, + {"value": "aliquot_of", "label": "aliquot_of"}, + {"value": "prepared_from", "label": "prepared_from"}, + {"value": "member_of", "label": "member_of"}, + {"value": "has_external_reference", "label": "has_external_reference"}, + {"value": "beta_extraction_batch_run", "label": "beta_extraction_batch_run"}, + {"value": "beta_extraction_run_input", "label": "beta_extraction_run_input"}, + {"value": "beta_extraction_plate", "label": "beta_extraction_plate"}, + {"value": "beta_extraction_well", "label": "beta_extraction_well"}, + {"value": "beta_extraction_output", "label": "beta_extraction_output"}, + {"value": "beta_extraction_run_output", "label": "beta_extraction_run_output"}, + {"value": "beta_post_extract_qc", "label": "beta_post_extract_qc"}, + {"value": "beta_library_prep_output", "label": "beta_library_prep_output"}, + {"value": "beta_library_material_output", "label": "beta_library_material_output"}, + {"value": "beta_library_qc", "label": "beta_library_qc"}, + {"value": "beta_pooling_run_output", "label": "beta_pooling_run_output"}, + {"value": "beta_pool_member", "label": "beta_pool_member"}, + {"value": "beta_pooling_run_input", "label": "beta_pooling_run_input"}, + {"value": "beta_sequencing_run", "label": "beta_sequencing_run"}, + {"value": "beta_sequenced_library_assignment", "label": "beta_sequenced_library_assignment"}, + {"value": "beta_assignment_source", "label": "beta_assignment_source"}, + {"value": "beta_assignment_library_material", "label": "beta_assignment_library_material"}, + {"value": "beta_assignment_barcode_reagent", "label": "beta_assignment_barcode_reagent"}, + {"value": "beta_run_artifact", "label": "beta_run_artifact"} + ] + }, + { + "name": "euids", + "label": "EUIDs (one per line)", + "type": "textarea", + "required": true, + "rows": 6 + } + ] } } } @@ -90,7 +141,8 @@ "method_name": "do_action_create_subject_and_anchor", "action_executed": "0", "max_executions": "-1", - "action_enabled": "1", + "action_enabled": "0", + "action_visible": "0", "capture_data": "yes", "captured_data": {}, "deactivate_actions_when_executed": [], diff --git a/bloom_lims/gui/actions.py b/bloom_lims/gui/actions.py index 32d7cdb..a41d394 100644 --- a/bloom_lims/gui/actions.py +++ b/bloom_lims/gui/actions.py @@ -4,6 +4,34 @@ from bloom_lims.domain import BloomObj +RELATIONSHIP_TYPE_OPTIONS = [ + {"value": "contains", "label": "contains"}, + {"value": "derived_from", "label": "derived_from"}, + {"value": "aliquot_of", "label": "aliquot_of"}, + {"value": "prepared_from", "label": "prepared_from"}, + {"value": "member_of", "label": "member_of"}, + {"value": "has_external_reference", "label": "has_external_reference"}, + {"value": "beta_extraction_batch_run", "label": "beta_extraction_batch_run"}, + {"value": "beta_extraction_run_input", "label": "beta_extraction_run_input"}, + {"value": "beta_extraction_plate", "label": "beta_extraction_plate"}, + {"value": "beta_extraction_well", "label": "beta_extraction_well"}, + {"value": "beta_extraction_output", "label": "beta_extraction_output"}, + {"value": "beta_extraction_run_output", "label": "beta_extraction_run_output"}, + {"value": "beta_post_extract_qc", "label": "beta_post_extract_qc"}, + {"value": "beta_library_prep_output", "label": "beta_library_prep_output"}, + {"value": "beta_library_material_output", "label": "beta_library_material_output"}, + {"value": "beta_library_qc", "label": "beta_library_qc"}, + {"value": "beta_pooling_run_output", "label": "beta_pooling_run_output"}, + {"value": "beta_pool_member", "label": "beta_pool_member"}, + {"value": "beta_pooling_run_input", "label": "beta_pooling_run_input"}, + {"value": "beta_sequencing_run", "label": "beta_sequencing_run"}, + {"value": "beta_sequenced_library_assignment", "label": "beta_sequenced_library_assignment"}, + {"value": "beta_assignment_source", "label": "beta_assignment_source"}, + {"value": "beta_assignment_library_material", "label": "beta_assignment_library_material"}, + {"value": "beta_assignment_barcode_reagent", "label": "beta_assignment_barcode_reagent"}, + {"value": "beta_run_artifact", "label": "beta_run_artifact"}, +] + def _normalize_action_slug(action_data: dict) -> str: method_name = str(action_data.get("method_name") or "").strip() @@ -46,8 +74,9 @@ def _default_ui_fields_for_action(action_data: dict) -> list[dict]: { "name": "relationship_type", "label": "Relationship Type", - "type": "text", + "type": "select", "required": True, + "options": RELATIONSHIP_TYPE_OPTIONS, }, { "name": "euids", @@ -74,9 +103,15 @@ def hydrate_dynamic_action_groups(action_groups: dict, bobdb: BloomObj) -> dict: actions = group_data.get("actions", {}) if not isinstance(actions, dict): continue - for action_data in actions.values(): + for action_key, action_data in list(actions.items()): if not isinstance(action_data, dict): continue + if ( + action_data.get("action_visible") == "0" + or _normalize_action_slug(action_data) == "create_subject_and_anchor" + ): + actions.pop(action_key, None) + continue captured = action_data.get("captured_data") if not isinstance(captured, dict): captured = {} diff --git a/bloom_lims/gui/routes/operations.py b/bloom_lims/gui/routes/operations.py index 518e540..550124b 100644 --- a/bloom_lims/gui/routes/operations.py +++ b/bloom_lims/gui/routes/operations.py @@ -547,13 +547,30 @@ async def update_preference(request: Request, auth: dict = Depends(require_auth) @router.get("/queue_details", response_class=HTMLResponse) async def queue_details( - request: Request, queue_euid, page=1, _auth=Depends(require_auth) + request: Request, + queue_euid: str | None = Query(default=None), + euid: str | None = Query(default=None), + page=1, + _auth=Depends(require_auth), ): + queue_euid = str(queue_euid or euid or "").strip() + user_data = request.session.get("user_data", {}) + if not queue_euid: + template = templates.get_template("modern/queue_retired.html") + return HTMLResponse( + content=template.render( + { + "request": request, + "udat": user_data, + "queue_euid": "", + } + ) + ) + page = int(page) if page < 1: page = 1 per_page = 500 - user_logged_in = True if "user_data" in request.session else False bobdb = BloomObj(BLOOMdb3(app_username=request.session["user_data"]["email"])) queue = bobdb.get_by_euid(queue_euid) qm = [] @@ -563,8 +580,6 @@ async def queue_details( queue_details = queue.sort_by_euid(qm) queue_details_list = queue_details[(page - 1) * per_page : page * per_page] pagination = {"next": page + 1, "prev": page - 1, "euid": queue_euid} - user_data = request.session.get("user_data", {}) - queue.items = queue_details_list template = templates.get_template("modern/queue_details.html") diff --git a/templates/modern/base.html b/templates/modern/base.html index f85249d..ecdb0da 100644 --- a/templates/modern/base.html +++ b/templates/modern/base.html @@ -63,9 +63,6 @@ Dashboard - - Queue Runtime - Equipment diff --git a/templates/modern/dashboard.html b/templates/modern/dashboard.html index 27ee403..dd56dc1 100644 --- a/templates/modern/dashboard.html +++ b/templates/modern/dashboard.html @@ -17,16 +17,6 @@

Dashboard

- -
- -
-
-
{{ stats.queue_runtime_total | default(0) }}
-
Queue Runtime Objects
-
-
-
@@ -60,37 +50,6 @@

Dashboard

- -
- -
- {% if recent_queue_runtime %} - - {% else %} -
-
-

No recent queue runtime objects

- Open Queue Runtime -
- {% endif %} -
-
-
@@ -129,7 +88,6 @@

Quick Actions

- Queue Runtime Equipment Reagents Templates diff --git a/templates/modern/euid_details.html b/templates/modern/euid_details.html index 2bcbaa0..56ec80f 100644 --- a/templates/modern/euid_details.html +++ b/templates/modern/euid_details.html @@ -160,9 +160,6 @@

Actions

Related Objects -
@@ -779,23 +776,27 @@

Full JSON (json_addl)

var editorProperties = null; var editorTemplate = null; -// Collapsible section toggle -document.querySelectorAll('.collapsible-btn').forEach(function(btn) { - btn.addEventListener('click', function() { - var targetId = this.getAttribute('data-target'); - var targetEl = document.getElementById(targetId); - - // Toggle active class on button - this.classList.toggle('active'); - - // Toggle visibility of content - if (targetEl.style.display === 'none') { - targetEl.style.display = 'block'; - } else { - targetEl.style.display = 'none'; - } +function bindCollapsibleSections() { + document.querySelectorAll('.collapsible-btn').forEach(function(btn) { + if (btn.dataset.bound === '1') return; + btn.dataset.bound = '1'; + btn.addEventListener('click', function() { + var targetId = this.getAttribute('data-target'); + var targetEl = document.getElementById(targetId); + if (!targetEl) { + if (typeof window.BloomToast !== 'undefined' && typeof window.BloomToast.error === 'function') { + window.BloomToast.error('Section Missing', 'Unable to find section ' + targetId); + } + return; + } + this.classList.toggle('active'); + targetEl.style.display = targetEl.style.display === 'none' || targetEl.style.display === '' ? 'block' : 'none'; + }); }); -}); +} + +bindCollapsibleSections(); +document.addEventListener('DOMContentLoaded', bindCollapsibleSections); // Format JSON on page load document.addEventListener('DOMContentLoaded', function() { @@ -957,11 +958,6 @@

Full JSON (json_addl)

} } -// Print label -function printLabel(euid) { - BloomToast.info('Print Label', 'Printing label for ' + euid); -} - // Edit mode state var isEditMode = false; var editorFull = null; diff --git a/templates/modern/queue_retired.html b/templates/modern/queue_retired.html new file mode 100644 index 0000000..316fa67 --- /dev/null +++ b/templates/modern/queue_retired.html @@ -0,0 +1,28 @@ +{% extends "modern/base.html" %} + +{% block title %}Queue Runtime Retired - BLOOM LIMS{% endblock %} + +{% block content %} + + +
+
+

Queue GUI Retired

+
+
+

+ Existing queue records are unchanged, but the Bloom dashboard no longer exposes queue runtime navigation. + Use object search or EUID details for retained Bloom objects. +

+ +
+
+{% endblock %} diff --git a/tests/test_action_button_template_safety.py b/tests/test_action_button_template_safety.py index be00720..b06b0a8 100644 --- a/tests/test_action_button_template_safety.py +++ b/tests/test_action_button_template_safety.py @@ -18,6 +18,13 @@ def test_modern_euid_details_uses_data_attribute_action_handler(): assert "onclick=\"showCapturedDataForm(this, {{ action_value | tojson }}" not in template +def test_modern_euid_details_has_no_toast_only_print_button(): + template = _read("templates/modern/euid_details.html") + + assert "onclick=\"printLabel('{{ obj.euid }}')\"" not in template + assert "Printing label for " not in template + + def test_modern_workflow_details_uses_data_attribute_action_handler(): template = _read("templates/modern/workflow_details.html") diff --git a/tests/test_action_schema_hydration.py b/tests/test_action_schema_hydration.py index 2b65717..2b508d1 100644 --- a/tests/test_action_schema_hydration.py +++ b/tests/test_action_schema_hydration.py @@ -44,3 +44,57 @@ def test_hydrate_dynamic_action_groups_removes_retired_assay_markup(): assert original_action["captured_data"] == {} assert "___workflow/assay/" not in hydrated_action["captured_data"] assert hydrated_action["ui_schema"]["fields"] == [] + + +def test_hydrate_dynamic_action_groups_adds_relationship_type_dropdown(): + action_groups = { + "core": { + "group_name": "Core Actions", + "actions": { + "action/core/add-relationships/1.0": { + "method_name": "do_action_add_relationships", + "capture_data": "yes", + "captured_data": {}, + "ui_schema": {"title": "Add Relationships", "fields": []}, + } + }, + } + } + + hydrated = hydrate_dynamic_action_groups(action_groups, SimpleNamespace()) + fields = hydrated["core"]["actions"]["action/core/add-relationships/1.0"][ + "ui_schema" + ]["fields"] + relationship_field = next( + field for field in fields if field["name"] == "relationship_type" + ) + + assert relationship_field["type"] == "select" + assert relationship_field["required"] is True + assert {"value": "derived_from", "label": "derived_from"} in relationship_field[ + "options" + ] + assert {"value": "beta_sequencing_run", "label": "beta_sequencing_run"} in relationship_field[ + "options" + ] + + +def test_hydrate_dynamic_action_groups_hides_subject_decision_scope_action(): + action_groups = { + "core": { + "group_name": "Core Actions", + "actions": { + "action/core/create-subject-and-anchor/1.0": { + "method_name": "do_action_create_subject_and_anchor", + "action_visible": "0", + "capture_data": "yes", + "captured_data": {}, + "ui_schema": {"title": "Create Subject", "fields": []}, + } + }, + } + } + + hydrated = hydrate_dynamic_action_groups(action_groups, SimpleNamespace()) + + assert hydrated["core"]["actions"] == {} diff --git a/tests/test_gui_endpoints.py b/tests/test_gui_endpoints.py index a290ad0..f7a117c 100644 --- a/tests/test_gui_endpoints.py +++ b/tests/test_gui_endpoints.py @@ -451,11 +451,11 @@ class TestQueueEndpoints: """Tests for queue management endpoints.""" def test_queue_details_returns_html(self, client): - """Test queue details returns HTML.""" + """Bare queue details route renders the retired queue GUI explanation.""" response = client.get("/queue_details") - assert response.status_code in [200, 422, 500] - if response.status_code == 200: - assert "text/html" in response.headers["content-type"] + assert response.status_code == 200 + assert "text/html" in response.headers["content-type"] + assert "Queue Runtime Hidden" in response.text def test_set_filter(self, client): """Test set filter endpoint works.""" @@ -653,11 +653,11 @@ def test_footer_has_version(self, client): assert "footer" in content.lower() def test_pages_have_breadcrumb_or_header(self, client): - """Queue runtime remains visible from the dashboard after assay-page retirement.""" + """Dashboard renders normal modern page header after queue GUI retirement.""" response = client.get("/") assert response.status_code == 200 content = response.text - assert "page-header" in content or "Queue Runtime" in content + assert "page-header" in content class TestModernAPIs: @@ -960,7 +960,7 @@ def test_dashboard_has_navigation_links(self, client): # Check for main navigation links assert 'href="/"' in content # Dashboard - assert 'href="/queue_details"' in content + assert 'href="/queue_details"' not in content assert 'href="/assays"' not in content assert 'href="/workflows"' not in content assert 'href="/admin"' in content @@ -1789,7 +1789,8 @@ class TestQueueEndpointsV2: def test_queue_details_no_params(self, client): """Test queue details without parameters.""" response = client.get("/queue_details") - assert response.status_code in [200, 302, 307, 400, 422, 500] + assert response.status_code == 200 + assert "Queue Runtime Hidden" in response.text def test_queue_details_with_euid(self, client): """Test queue details with EUID.""" From a2b6d5706b9878648db60d9e8ce0b0fd5beeda27 Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 4 Jun 2026 13:35:17 -0700 Subject: [PATCH 09/47] Release Bloom 6.0.0 TapDB UI updates --- ...604T110700Z_access_logging_ai_agent_gap.md | 24 +++++ pyproject.toml | 2 +- static/js/graph.js | 42 +++++++-- static/modern/css/bloom_modern.css | 93 +++++++++++++++++++ static/modern/js/lsmc_ui.js | 74 +++++++++++++++ templates/modern/base.html | 3 +- templates/modern/dag_explorer.html | 11 ++- tests/test_cytoscape_dag.py | 12 +++ uv.lock | 16 ++-- 9 files changed, 258 insertions(+), 19 deletions(-) create mode 100644 docs/plans/20260604T110700Z_access_logging_ai_agent_gap.md create mode 100644 static/modern/js/lsmc_ui.js diff --git a/docs/plans/20260604T110700Z_access_logging_ai_agent_gap.md b/docs/plans/20260604T110700Z_access_logging_ai_agent_gap.md new file mode 100644 index 0000000..147eee8 --- /dev/null +++ b/docs/plans/20260604T110700Z_access_logging_ai_agent_gap.md @@ -0,0 +1,24 @@ +# Bloom Access Logging And AI-Agent Read Access Gap + +Created: 2026-06-04T11:07:00Z + +## Status + +Bloom needs a follow-up implementation pass before Kahlo-issued AI-agent tokens can safely read Bloom search endpoints directly. + +## Required Contract + +- Validate Kahlo-issued AI-agent bearer tokens against an explicit Dayhoff-generated allowlist. +- Accept only read-only endpoint IDs approved for Bloom search/detail APIs. +- Record every endpoint access with request ID, correlation ID, route template, status, duration, client IP, auth mode, human user, service ID, AI-agent ID, authorizing human, token ID prefix/hash, scopes, and denial reason. +- Never log bearer tokens, cookies, raw presigned URLs, PHI-bearing query strings, object keys, or raw request/response bodies. + +## Current Gap + +Current source does not prove uniform all-endpoint structured access logs with actor/IP/AI-agent provenance, and it does not validate Kahlo-issued AI-agent tokens on Bloom search/read APIs. + +## Acceptance + +- Focused tests prove allowed AI-agent tokens can read only approved Bloom search/detail endpoints. +- Mutating endpoints and non-allowlisted reads reject AI-agent tokens. +- Access-log tests prove actor/IP/request/token provenance is present and sensitive values are redacted. diff --git a/pyproject.toml b/pyproject.toml index 65559a0..f3fff21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ license = {text = "MIT"} requires-python = ">=3.12" dependencies = [ "cli-core-yo==2.1.1", - "daylily-tapdb>=7.0.5,<8.0.0", + "daylily-tapdb==8.0.1", "daylily-auth-cognito==2.1.5", "IPython>=8.18.1", "bandit[toml]>=1.8.0", diff --git a/static/js/graph.js b/static/js/graph.js index 9ebbb86..b6eae15 100644 --- a/static/js/graph.js +++ b/static/js/graph.js @@ -27,6 +27,7 @@ const keyState = { c: false, n: false, i: false, + z: false, }; const DEFAULT_CONTROL_STATE = { @@ -160,13 +161,13 @@ const cytoscapeStyle = [ "width": 2, "line-color": "#666", "curve-style": "bezier", - "source-arrow-shape": "none", - "target-arrow-shape": "triangle", - "target-arrow-fill": "filled", - "target-arrow-color": "#666", + "source-arrow-shape": "triangle", + "source-arrow-fill": "filled", + "source-arrow-color": "#666", + "target-arrow-shape": "none", "source-endpoint": "outside-to-node", "target-endpoint": "outside-to-node", - "target-distance-from-node": 6, + "source-distance-from-node": 6, "arrow-scale": 1.6, "opacity": 1, }, @@ -176,7 +177,7 @@ const cytoscapeStyle = [ style: { "line-style": "dashed", "line-color": "#c1a967", - "target-arrow-color": "#c1a967", + "source-arrow-color": "#c1a967", }, }, { @@ -184,7 +185,7 @@ const cytoscapeStyle = [ style: { "line-style": "dotted", "line-color": "#f7c948", - "target-arrow-color": "#f7c948", + "source-arrow-color": "#f7c948", "width": 3, }, }, @@ -192,7 +193,7 @@ const cytoscapeStyle = [ selector: "edge:selected", style: { "line-color": "#e74c3c", - "target-arrow-color": "#e74c3c", + "source-arrow-color": "#e74c3c", "width": 3, }, }, @@ -385,6 +386,9 @@ function installKeyboardHandlers() { if (key === "i") { keyState.i = true; } + if (key === "z") { + keyState.z = true; + } if (key === "escape") { clearPendingLineageSelection(); setStatus("Cleared selection.", "warn"); @@ -411,9 +415,30 @@ function installKeyboardHandlers() { if (key === "i") { keyState.i = false; } + if (key === "z") { + keyState.z = false; + } }); } +function configureCytoscapeInteractions(container) { + if (container.dataset.cytoscapeWheelGate === "installed") { + return; + } + container.dataset.cytoscapeWheelGate = "installed"; + container.addEventListener( + "wheel", + (evt) => { + const isPinchZoom = evt.ctrlKey || evt.metaKey; + if (isPinchZoom || keyState.z) { + return; + } + evt.stopImmediatePropagation(); + }, + { capture: true, passive: true }, + ); +} + function registerTapSequence(nodeId, button) { const key = `${nodeId}|${button}`; const now = Date.now(); @@ -1081,6 +1106,7 @@ function initCytoscape(container, elements) { evt.preventDefault(); }); + configureCytoscapeInteractions(container); cy = cytoscape({ container: container, elements: elements, diff --git a/static/modern/css/bloom_modern.css b/static/modern/css/bloom_modern.css index 3b9e612..294a908 100644 --- a/static/modern/css/bloom_modern.css +++ b/static/modern/css/bloom_modern.css @@ -1121,3 +1121,96 @@ pre { white-space: nowrap; border-width: 0; } +.lsmc-theme-control, +.lsmc-action-help-button, +.lsmc-action-help-panel { + font-family: var(--font-sans, Inter, system-ui, sans-serif); + z-index: 2147483000; +} + +.lsmc-theme-control { + position: fixed; + right: 1rem; + bottom: 5rem; + padding: 0.4rem 0.55rem; + border: 1px solid color-mix(in srgb, currentColor 20%, transparent); + border-radius: 8px; + background: color-mix(in srgb, Canvas 94%, transparent); + box-shadow: 0 10px 28px rgba(15, 23, 42, 0.16); + font-size: 0.78rem; +} + +.lsmc-theme-control label { + display: flex; + gap: 0.4rem; + align-items: center; +} + +.lsmc-theme-control select { + min-height: 1.7rem; + border-radius: 6px; +} + +.lsmc-action-help-button { + position: fixed; + right: 1rem; + bottom: 1rem; + width: 2.65rem; + height: 2.65rem; + border: 0; + border-radius: 999px; + background: #1f6feb; + color: white; + font-weight: 800; + box-shadow: 0 12px 30px rgba(15, 23, 42, 0.22); +} + +.lsmc-action-help-panel { + position: fixed; + right: 1rem; + bottom: 4.1rem; + width: min(26rem, calc(100vw - 2rem)); + padding: 0.8rem; + border: 1px solid color-mix(in srgb, currentColor 20%, transparent); + border-radius: 8px; + background: Canvas; + color: CanvasText; + box-shadow: 0 18px 44px rgba(15, 23, 42, 0.24); +} + +.lsmc-action-help-panel pre { + overflow: auto; + margin: 0.55rem 0; + padding: 0.65rem; + border-radius: 6px; + background: color-mix(in srgb, CanvasText 8%, Canvas); + white-space: pre-wrap; +} + +html[data-theme="lsmc"] body { + background: #f4f8fb; + color: #172033; +} + +html[data-theme="dark"] { + color-scheme: dark; +} + +html[data-theme="dark"] body { + background: #10141d; + color: #e5edf7; +} + +html[data-theme="light"] body { + background: #ffffff; + color: #111827; +} + +html[data-theme="tacky"] body { + background: #fff700; + color: #1f1147; +} + +html[data-theme="tacky"] .lsmc-action-help-button { + background: #ff00b8; +} diff --git a/static/modern/js/lsmc_ui.js b/static/modern/js/lsmc_ui.js new file mode 100644 index 0000000..d2e2a7c --- /dev/null +++ b/static/modern/js/lsmc_ui.js @@ -0,0 +1,74 @@ +(function () { + const themes = ["original", "lsmc", "dark", "light", "tacky"]; + const storageKey = "lsmc.ui.theme"; + + function currentTheme() { + const stored = window.localStorage.getItem(storageKey); + return themes.includes(stored) ? stored : "original"; + } + + function applyTheme(theme) { + const value = themes.includes(theme) ? theme : "original"; + document.documentElement.dataset.theme = value; + window.localStorage.setItem(storageKey, value); + } + + function commandForPage() { + const explicit = document.body.dataset.actionHelpCommand || ""; + if (explicit) return explicit; + if (location.pathname.startsWith("/search")) return "bloom search --help"; + if (location.pathname.startsWith("/create_object")) return "bloom object create --help"; + if (location.pathname.startsWith("/admin")) return "bloom admin --help"; + if (location.pathname.startsWith("/dag")) return "bloom dag show --help"; + return `No CLI equivalent for bloom ${location.pathname}`; + } + + function initThemeControl() { + const wrap = document.createElement("div"); + wrap.className = "lsmc-theme-control"; + const label = document.createElement("label"); + label.textContent = "Theme"; + const select = document.createElement("select"); + for (const theme of themes) { + const option = document.createElement("option"); + option.value = theme; + option.textContent = theme; + select.appendChild(option); + } + select.value = currentTheme(); + select.addEventListener("change", () => applyTheme(select.value)); + label.appendChild(select); + wrap.appendChild(label); + document.body.appendChild(wrap); + } + + function initActionHelp() { + const button = document.createElement("button"); + button.type = "button"; + button.className = "lsmc-action-help-button"; + button.setAttribute("aria-expanded", "false"); + button.textContent = "?"; + const panel = document.createElement("aside"); + panel.className = "lsmc-action-help-panel"; + panel.hidden = true; + panel.innerHTML = 'Action Help
';
+    const output = panel.querySelector("pre");
+    const copy = panel.querySelector("button");
+    function refresh() {
+      output.textContent = commandForPage();
+    }
+    button.addEventListener("click", () => {
+      panel.hidden = !panel.hidden;
+      button.setAttribute("aria-expanded", String(!panel.hidden));
+      refresh();
+    });
+    copy.addEventListener("click", () => navigator.clipboard?.writeText(output.textContent || ""));
+    document.body.append(button, panel);
+  }
+
+  applyTheme(currentTheme());
+  document.addEventListener("DOMContentLoaded", () => {
+    initThemeControl();
+    initActionHelp();
+  });
+})();
diff --git a/templates/modern/base.html b/templates/modern/base.html
index ecdb0da..5fafe55 100644
--- a/templates/modern/base.html
+++ b/templates/modern/base.html
@@ -1,5 +1,5 @@
 
-
+
 
     
     
@@ -156,6 +156,7 @@
     
     
     
+    
 
     
     
+{% endblock %}
diff --git a/tests/test_bloom_prefix_taxonomy.py b/tests/test_bloom_prefix_taxonomy.py
index 856a4ff..12b3181 100644
--- a/tests/test_bloom_prefix_taxonomy.py
+++ b/tests/test_bloom_prefix_taxonomy.py
@@ -62,6 +62,7 @@ def test_wet_lab_templates_use_bloom_prefix_taxonomy() -> None:
         "data/library-index-assignment/sequencing-library/1.0": "BDA",
         "data/execution/transfer/1.0": "BDT",
         "generic/generic/external_object_link/1.0": "BGX",
+        "set/run-set/generic/1.01": "BGS",
     }
 
     for code, expected_prefix in expected_prefixes.items():
diff --git a/tests/test_lab_actions_api.py b/tests/test_lab_actions_api.py
new file mode 100644
index 0000000..b5f3953
--- /dev/null
+++ b/tests/test_lab_actions_api.py
@@ -0,0 +1,566 @@
+"""API coverage for Bloom lab actions."""
+
+from __future__ import annotations
+
+import os
+from unittest.mock import patch
+
+os.environ["BLOOM_DEV_AUTH_BYPASS"] = "true"
+os.environ["BLOOM_OAUTH"] = "no"
+
+from fastapi.testclient import TestClient
+
+from main import app
+
+
+def _client() -> TestClient:
+    return TestClient(app)
+
+
+def _create_object(
+    client: TestClient,
+    *,
+    category: str,
+    type_name: str,
+    subtype: str,
+    name: str,
+    version: str = "1.0",
+    properties: dict | None = None,
+    count: int = 1,
+) -> dict:
+    response = client.post(
+        "/api/v1/object-creation/create",
+        json={
+            "category": category,
+            "type": type_name,
+            "subtype": subtype,
+            "version": version,
+            "name": name,
+            "properties": properties or {},
+            "count": count,
+        },
+    )
+    assert response.status_code == 200, response.text
+    return response.json()
+
+
+def _link_material(client: TestClient, *, container_euid: str, content_euid: str) -> None:
+    response = client.post(
+        "/api/v1/lineages/",
+        json={
+            "parent_euid": container_euid,
+            "child_euid": content_euid,
+            "relationship_type": "HOLDS_MATERIAL",
+        },
+    )
+    assert response.status_code == 200, response.text
+
+
+def _create_filled_tube(client: TestClient, *, suffix: str) -> tuple[str, str]:
+    with patch(
+        "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None
+    ):
+        tube = _create_object(
+            client,
+            category="container",
+            type_name="tube",
+            subtype="tube-generic-10ml",
+            name=f"pytest incoming tube {suffix}",
+        )
+    content = _create_object(
+        client,
+        category="content",
+        type_name="specimen",
+        subtype="buccal-swab",
+        name=f"pytest buccal specimen {suffix}",
+    )
+    _link_material(client, container_euid=tube["euid"], content_euid=content["euid"])
+    return tube["euid"], content["euid"]
+
+
+def _create_plate(client: TestClient, *, name: str) -> str:
+    with patch(
+        "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None
+    ):
+        plate = _create_object(
+            client,
+            category="container",
+            type_name="plate",
+            subtype="fixed-plate-96",
+            name=name,
+        )
+    return plate["euid"]
+
+
+def _create_reagent_index(client: TestClient, *, name: str) -> str:
+    reagent = _create_object(
+        client,
+        category="content",
+        type_name="reagent",
+        subtype="sequencing-index",
+        name=name,
+    )
+    return reagent["euid"]
+
+
+def _create_instrument(client: TestClient, *, name: str) -> str:
+    instrument = _create_object(
+        client,
+        category="equipment",
+        type_name="sequencers",
+        subtype="novaseq-6000",
+        name=name,
+    )
+    return instrument["euid"]
+
+
+def test_lab_actions_full_ilmn_flow_and_csv_export() -> None:
+    client = _client()
+    tube_1, _ = _create_filled_tube(client, suffix="a")
+    tube_2, _ = _create_filled_tube(client, suffix="b")
+
+    with patch(
+        "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None
+    ):
+        extraction = client.post(
+            "/api/v1/lab-actions/extraction-plates",
+            json={
+                "mode": "auto",
+                "tube_euids": [tube_1, tube_2],
+                "plate_name": "pytest extraction plate",
+                "create_run_set": True,
+            },
+        )
+    assert extraction.status_code == 200, extraction.text
+    extraction_payload = extraction.json()
+    assert extraction_payload["plate_euid"]
+    assert extraction_payload["run_set_euid"]
+    assert [item["well_name"] for item in extraction_payload["mappings"]] == [
+        "A1",
+        "A2",
+    ]
+    assert all(item["output_content_euid"] for item in extraction_payload["mappings"])
+
+    plate_csv = client.get(
+        f"/api/v1/lab-actions/plates/{extraction_payload['plate_euid']}/mapping.csv"
+    )
+    assert plate_csv.status_code == 200, plate_csv.text
+    assert "tube_euid,plate_euid,well_euid,well_row,well_col" in plate_csv.text
+    assert tube_1 in plate_csv.text
+
+    with patch(
+        "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None
+    ):
+        library = client.post(
+            "/api/v1/lab-actions/seq-library-plates",
+            json={
+                "mode": "plate_1_to_1",
+                "source_plate_euid": extraction_payload["plate_euid"],
+                "plate_name": "pytest library plate",
+            },
+        )
+    assert library.status_code == 200, library.text
+    library_payload = library.json()
+    assert library_payload["plate_euid"]
+    assert len(library_payload["mappings"]) == 2
+    library_contents = [
+        item["library_content_euid"] for item in library_payload["mappings"]
+    ]
+
+    with patch(
+        "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None
+    ):
+        pool = client.post(
+            "/api/v1/lab-actions/seq-library-pools",
+            json={
+                "input_euids": library_contents,
+                "platform": "ILMN",
+                "pool_name": "pytest sequencing pool",
+            },
+        )
+    assert pool.status_code == 200, pool.text
+    pool_payload = pool.json()
+    assert pool_payload["pool_tube_euid"]
+    assert pool_payload["pool_content_euid"]
+    assert pool_payload["member_euids"] == library_contents
+
+    seq_run = client.post(
+        "/api/v1/lab-actions/seq-runs",
+        json={
+            "pool_tube_euid": pool_payload["pool_tube_euid"],
+            "pool_content_euid": pool_payload["pool_content_euid"],
+            "platform": "ILMN",
+            "operator": "pytest@example.com",
+            "flowcell_barcode": "PYTEST-FLOWCELL",
+            "status": "created",
+        },
+    )
+    assert seq_run.status_code == 200, seq_run.text
+    run_payload = seq_run.json()
+    assert run_payload["set_euid"]
+
+    sample_sheet = client.get(
+        f"/api/v1/lab-actions/seq-runs/{run_payload['set_euid']}/samplesheet"
+    )
+    assert sample_sheet.status_code == 200, sample_sheet.text
+    assert "[Header]" in sample_sheet.text
+    assert f"RunSetEUID,{run_payload['set_euid']}" in sample_sheet.text
+    assert "PYTEST-FLOWCELL" in sample_sheet.text
+    for library_content in library_contents:
+        assert library_content in sample_sheet.text
+
+
+def test_lab_actions_validation_and_non_ilmn_samplesheet_rejection() -> None:
+    client = _client()
+    invalid = client.post(
+        "/api/v1/lab-actions/extraction-plates",
+        json={"mode": "auto", "tube_euids": []},
+    )
+    assert invalid.status_code == 422
+
+    tube, _ = _create_filled_tube(client, suffix="non-ilmn")
+    with patch(
+        "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None
+    ):
+        pool = client.post(
+            "/api/v1/lab-actions/seq-library-pools",
+            json={
+                "input_euids": [tube],
+                "platform": "ONT",
+                "pool_name": "pytest ont pool",
+            },
+        )
+    assert pool.status_code == 200, pool.text
+    run = client.post(
+        "/api/v1/lab-actions/seq-runs",
+        json={
+            "pool_tube_euid": pool.json()["pool_tube_euid"],
+            "pool_content_euid": pool.json()["pool_content_euid"],
+            "platform": "ONT",
+            "flowcell_barcode": "ONT-FLOWCELL",
+        },
+    )
+    assert run.status_code == 200, run.text
+    sample_sheet = client.get(
+        f"/api/v1/lab-actions/seq-runs/{run.json()['set_euid']}/samplesheet"
+    )
+    assert sample_sheet.status_code == 400
+    assert "only implemented for ILMN" in sample_sheet.json()["detail"]
+
+
+def test_object_creation_count_and_print_euid_mock() -> None:
+    client = _client()
+    created = _create_object(
+        client,
+        category="set",
+        type_name="run-set",
+        subtype="generic",
+        version="1.01",
+        name="pytest run set",
+        count=3,
+    )
+    assert len(created["created_euids"]) == 3
+    assert created["euid"] == created["created_euids"][0]
+
+    with patch("bloom_lims.domain.lab_actions.ZebraDayService") as service_cls:
+        service = service_cls.return_value
+        service.submit_print_job.side_effect = [
+            {"job_euid": f"JOB-{index}", "status": "queued"} for index in range(3)
+        ]
+        response = client.post(
+            "/api/v1/lab-actions/print-euids",
+            json={
+                "euids": created["created_euids"],
+                "lab": "SSF",
+                "printer_id": "pytest-printer",
+                "label_zpl_style": "euid",
+                "copies": 1,
+            },
+        )
+    assert response.status_code == 200, response.text
+    assert response.json()["printed"] == 3
+    assert service.submit_print_job.call_count == 3
+
+    recursive_batch = client.post(
+        "/api/v1/object-creation/create",
+        json={
+            "category": "container",
+            "type": "plate",
+            "subtype": "fixed-plate-96",
+            "version": "1.0",
+            "name": "unsafe recursive plate batch",
+            "count": 2,
+        },
+    )
+    assert recursive_batch.status_code == 400
+    assert "recursive templates" in recursive_batch.json()["detail"]
+
+
+def test_lab_actions_gui_renders_wizard() -> None:
+    client = _client()
+    response = client.get("/lab-actions")
+    assert response.status_code == 200, response.text
+    html = response.text
+    assert "Lab Actions" in html
+    assert "Extraction Plate" in html
+    assert "Sequencing-Library Plate" in html
+    assert "Sequencing Pool Tube" in html
+    assert "Sequencing Run Set" in html
+    assert "/api/v1/lab-actions/extraction-plates" in html
+
+
+def test_directed_extraction_into_existing_plate_with_quant_and_reuse_guard() -> None:
+    client = _client()
+    existing_plate_euid = _create_plate(client, name="pytest existing extraction plate")
+    tube, _ = _create_filled_tube(client, suffix="directed")
+
+    with patch(
+        "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None
+    ):
+        response = client.post(
+            "/api/v1/lab-actions/extraction-plates",
+            json={
+                "mode": "directed",
+                "plate_euid": existing_plate_euid,
+                "assignments": [
+                    {
+                        "tube_euid": tube,
+                        "row": "B",
+                        "col": 3,
+                        "quant": {
+                            "method": "Qubit",
+                            "concentration": 42.5,
+                            "concentration_units": "ng/uL",
+                        },
+                    }
+                ],
+                "run_set": {
+                    "name": "pytest directed extraction set",
+                    "members": [tube],
+                    "external_members": ["atlas:pytest-external-member"],
+                    "metadata": {"purpose": "directed-test"},
+                },
+            },
+        )
+    assert response.status_code == 200, response.text
+    payload = response.json()
+    assert payload["plate_euid"] == existing_plate_euid
+    assert payload["new_container_euids"] == []
+    assert payload["run_set_euid"]
+    assert payload["mappings"][0]["well_name"] == "B3"
+    assert payload["mappings"][0]["quant_euid"]
+
+    repeat = client.post(
+        "/api/v1/lab-actions/extraction-plates",
+        json={
+            "mode": "directed",
+            "plate_euid": existing_plate_euid,
+            "assignments": [{"tube_euid": tube, "row": "B", "col": 3}],
+        },
+    )
+    assert repeat.status_code == 400
+    assert "already filled" in repeat.json()["detail"]
+
+
+def test_directed_extraction_rejects_duplicate_tubes_and_wells() -> None:
+    client = _client()
+    tube_1, _ = _create_filled_tube(client, suffix="dup-1")
+    tube_2, _ = _create_filled_tube(client, suffix="dup-2")
+
+    duplicate_tube = client.post(
+        "/api/v1/lab-actions/extraction-plates",
+        json={
+            "mode": "directed",
+            "assignments": [
+                {"tube_euid": tube_1, "row": "A", "col": 1},
+                {"tube_euid": tube_1, "row": "A", "col": 2},
+            ],
+        },
+    )
+    assert duplicate_tube.status_code == 400
+    assert "duplicate assignment tube_euid" in duplicate_tube.json()["detail"]
+
+    duplicate_well = client.post(
+        "/api/v1/lab-actions/extraction-plates",
+        json={
+            "mode": "directed",
+            "assignments": [
+                {"tube_euid": tube_1, "row": "A", "col": 1},
+                {"tube_euid": tube_2, "row": "A", "col": 1},
+            ],
+        },
+    )
+    assert duplicate_well.status_code == 400
+    assert "duplicate destination well" in duplicate_well.json()["detail"]
+
+
+def test_directed_library_plate_with_index_and_run_set() -> None:
+    client = _client()
+    tube, _ = _create_filled_tube(client, suffix="library-directed")
+    extraction = client.post(
+        "/api/v1/lab-actions/extraction-plates",
+        json={"mode": "auto", "tube_euids": [tube]},
+    )
+    assert extraction.status_code == 200, extraction.text
+    source_well = extraction.json()["mappings"][0]["well_euid"]
+    index_euid = _create_reagent_index(client, name="pytest sequencing index")
+
+    with patch(
+        "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None
+    ):
+        response = client.post(
+            "/api/v1/lab-actions/seq-library-plates",
+            json={
+                "mode": "directed",
+                "plate_name": "pytest directed library plate",
+                "assignments": [
+                    {
+                        "source_well_euid": source_well,
+                        "row": "C",
+                        "col": 4,
+                        "index_barcode": "ACGTACGT",
+                        "index_euid": index_euid,
+                        "data": {"cycles": 8},
+                    }
+                ],
+                "create_run_set": True,
+                "run_set": {"name": "pytest library run set"},
+            },
+        )
+    assert response.status_code == 200, response.text
+    payload = response.json()
+    assert payload["run_set_euid"]
+    assert payload["mappings"][0]["well_name"] == "C4"
+    assert payload["mappings"][0]["index_barcode"] == "ACGTACGT"
+    assert payload["mappings"][0]["index_euid"] == index_euid
+
+    duplicate_destination = client.post(
+        "/api/v1/lab-actions/seq-library-plates",
+        json={
+            "mode": "directed",
+            "assignments": [
+                {"source_well_euid": source_well, "row": "A", "col": 1},
+                {"source_well_euid": source_well, "row": "A", "col": 1},
+            ],
+        },
+    )
+    assert duplicate_destination.status_code == 400
+    assert "duplicate destination well" in duplicate_destination.json()["detail"]
+
+
+def test_pool_existing_tube_pool_from_pool_and_seq_run_links() -> None:
+    client = _client()
+    tube, _ = _create_filled_tube(client, suffix="pool-existing")
+    extraction = client.post(
+        "/api/v1/lab-actions/extraction-plates",
+        json={"mode": "auto", "tube_euids": [tube]},
+    )
+    assert extraction.status_code == 200, extraction.text
+    library = client.post(
+        "/api/v1/lab-actions/seq-library-plates",
+        json={
+            "mode": "plate_1_to_1",
+            "source_plate_euid": extraction.json()["plate_euid"],
+        },
+    )
+    assert library.status_code == 200, library.text
+    library_content = library.json()["mappings"][0]["library_content_euid"]
+
+    existing_pool_tube = _create_object(
+        client,
+        category="container",
+        type_name="tube",
+        subtype="tube-generic-10ml",
+        name="pytest existing pool tube",
+    )["euid"]
+    first_pool = client.post(
+        "/api/v1/lab-actions/seq-library-pools",
+        json={
+            "input_euids": [library_content],
+            "pool_tube_euid": existing_pool_tube,
+            "platform": "ILMN",
+            "pool_name": "pytest first pool",
+            "create_run_set": True,
+        },
+    )
+    assert first_pool.status_code == 200, first_pool.text
+    first_payload = first_pool.json()
+    assert first_payload["pool_tube_euid"] == existing_pool_tube
+    assert first_payload["new_container_euids"] == []
+    assert first_payload["run_set_euid"]
+
+    second_pool = client.post(
+        "/api/v1/lab-actions/seq-library-pools",
+        json={
+            "input_euids": [first_payload["pool_tube_euid"]],
+            "platform": "ILMN",
+            "pool_name": "pytest pool from pool",
+        },
+    )
+    assert second_pool.status_code == 200, second_pool.text
+    assert second_pool.json()["member_euids"] == [first_payload["pool_content_euid"]]
+
+    duplicate_inputs = client.post(
+        "/api/v1/lab-actions/seq-library-pools",
+        json={
+            "input_euids": [library_content, library_content],
+            "platform": "ILMN",
+        },
+    )
+    assert duplicate_inputs.status_code == 400
+    assert "duplicate input_euids" in duplicate_inputs.json()["detail"]
+
+    instrument_euid = _create_instrument(client, name="pytest novaseq")
+    reagent_euid = _create_reagent_index(client, name="pytest run reagent")
+    seq_run = client.post(
+        "/api/v1/lab-actions/seq-runs",
+        json={
+            "pool_tube_euid": second_pool.json()["pool_tube_euid"],
+            "platform": "ILMN",
+            "operator": "pytest@example.com",
+            "instrument_euid": instrument_euid,
+            "machine": "NovaSeq 6000",
+            "flowcell_barcode": "FLOWCELL-LINKS",
+            "reagent_euids": [reagent_euid],
+            "status": "running",
+            "metadata": {"purpose": "link coverage"},
+        },
+    )
+    assert seq_run.status_code == 200, seq_run.text
+    assert seq_run.json()["status"] == "running"
+
+
+def test_lab_action_schema_validation_edges() -> None:
+    client = _client()
+    bad_auto = client.post(
+        "/api/v1/lab-actions/extraction-plates",
+        json={
+            "mode": "auto",
+            "tube_euids": ["Z-BCT-1"],
+            "assignments": [{"tube_euid": "Z-BCT-2", "row": "A", "col": 1}],
+        },
+    )
+    assert bad_auto.status_code == 422
+
+    bad_directed = client.post(
+        "/api/v1/lab-actions/extraction-plates",
+        json={"mode": "directed", "tube_euids": ["Z-BCT-1"]},
+    )
+    assert bad_directed.status_code == 422
+
+    bad_library = client.post(
+        "/api/v1/lab-actions/seq-library-plates",
+        json={"mode": "plate_1_to_1"},
+    )
+    assert bad_library.status_code == 422
+
+    too_many_prints = client.post(
+        "/api/v1/lab-actions/print-euids",
+        json={
+            "euids": [f"Z-BCT-{index}" for index in range(501)],
+            "lab": "SSF",
+            "printer_id": "pytest-printer",
+            "label_zpl_style": "euid",
+        },
+    )
+    assert too_many_prints.status_code == 422
diff --git a/tests/test_route_coverage_gaps_api.py b/tests/test_route_coverage_gaps_api.py
index e992fb4..f42f170 100644
--- a/tests/test_route_coverage_gaps_api.py
+++ b/tests/test_route_coverage_gaps_api.py
@@ -113,6 +113,45 @@ def test_object_creation_creates_all_sequencing_run_platform_prefixes(
         assert created["euid"].startswith(f"{domain_code}-{prefix}-")
 
 
+def test_object_creation_plate_creates_96_linked_wells(
+    client: TestClient, bdb
+) -> None:
+    created = _create_instance_via_object_creation(
+        client,
+        category="container",
+        type_name="plate",
+        subtype="fixed-plate-96",
+        version="1.0",
+        name="pytest-fixed-96",
+    )
+    plate_euid = created["euid"]
+
+    lineage_cls = bdb.Base.classes.generic_instance_lineage
+    instance_cls = bdb.Base.classes.generic_instance
+    plate = bdb.session.query(instance_cls).filter_by(euid=plate_euid).one()
+    lineages = (
+        bdb.session.query(lineage_cls)
+        .filter(
+            lineage_cls.parent_instance_uid == plate.uid,
+            lineage_cls.relationship_type == "contains",
+            lineage_cls.is_deleted.is_(False),
+        )
+        .all()
+    )
+    wells = [lineage.child_instance for lineage in lineages]
+    positions = {
+        ((well.json_addl or {}).get("cont_address") or {}).get("name")
+        for well in wells
+    }
+    assert len(wells) == 96
+    assert {well.type for well in wells} == {"well"}
+    assert {"A1", "H12"}.issubset(positions)
+
+    layout = client.get(f"/api/v1/containers/{plate_euid}/layout")
+    assert layout.status_code == 200, layout.text
+    assert layout.json()["well_count"] == 96
+
+
 def test_actions_endpoints_execute_handler_body(client: TestClient) -> None:
     resp = client.post(
         "/api/v1/actions/aliquot",
diff --git a/tests/test_template_instantiation_recursion.py b/tests/test_template_instantiation_recursion.py
new file mode 100644
index 0000000..752c999
--- /dev/null
+++ b/tests/test_template_instantiation_recursion.py
@@ -0,0 +1,185 @@
+from __future__ import annotations
+
+import logging
+from types import SimpleNamespace
+
+import pytest
+
+from bloom_lims.domain.base import BloomObj
+
+
+class _FakeLineage:
+    def __init__(self, **kwargs):
+        self.__dict__.update(kwargs)
+
+
+class _FakeSession:
+    def __init__(self):
+        self.added = []
+        self.commit_count = 0
+
+    def add(self, obj):
+        self.added.append(obj)
+
+    def commit(self):
+        self.commit_count += 1
+
+
+def _template(euid, category, type_name, subtype, *, layouts=None):
+    return SimpleNamespace(
+        euid=euid,
+        uid=hash(euid) & 0xFFFF,
+        name=euid,
+        category=category,
+        type=type_name,
+        subtype=subtype,
+        version="1.0",
+        bstatus="active",
+        json_addl={"instantiation_layouts": layouts or {}},
+        polymorphic_discriminator=f"{category}_template",
+    )
+
+
+def _fake_bloom_obj(templates):
+    obj = BloomObj.__new__(BloomObj)
+    obj.logger = logging.getLogger("test-template-instantiation")
+    obj.session = _FakeSession()
+    obj.Base = SimpleNamespace(
+        classes=SimpleNamespace(generic_instance_lineage=_FakeLineage)
+    )
+    obj.domain_code = "Z"
+    created = {"count": 0, "instances": {}}
+    templates_by_euid = {template.euid: template for template in templates}
+
+    def get_by_euid(euid):
+        return templates_by_euid.get(euid) or created["instances"].get(euid)
+
+    def query_template_by_component_v2(category=None, type=None, subtype=None, version=None):
+        return [
+            template
+            for template in templates
+            if (category is None or template.category == category)
+            and (type is None or template.type == type)
+            and (subtype is None or template.subtype == subtype)
+            and (version is None or template.version == version)
+        ]
+
+    def create_instance(template_euid, json_addl_overrides=None):
+        template = templates_by_euid[template_euid]
+        created["count"] += 1
+        instance = SimpleNamespace(
+            uid=created["count"],
+            euid=f"I-{created['count']}",
+            name=(json_addl_overrides or {})
+            .get("properties", {})
+            .get("name", template.name),
+            category=template.category,
+            type=template.type,
+            subtype=template.subtype,
+            version=template.version,
+            bstatus=template.bstatus,
+            json_addl=json_addl_overrides or {},
+            polymorphic_discriminator=f"{template.category}_instance",
+        )
+        created["instances"][instance.euid] = instance
+        return instance
+
+    obj.get_by_euid = get_by_euid
+    obj.query_template_by_component_v2 = query_template_by_component_v2
+    obj.create_instance = create_instance
+    return obj
+
+
+def test_create_instances_recurses_nested_instantiation_layouts():
+    leaf = _template("TPL-LEAF", "content", "material", "leaf")
+    mid = _template(
+        "TPL-MID",
+        "container",
+        "well",
+        "middle",
+        layouts=[
+            {
+                "relationship_type": "HOLDS_MATERIAL",
+                "child_templates": [
+                    {
+                        "template_code": "content/material/leaf/1.0/",
+                        "json_addl": {"properties": {"name": "leaf child"}},
+                    }
+                ],
+            }
+        ],
+    )
+    root = _template(
+        "TPL-ROOT",
+        "container",
+        "plate",
+        "nested",
+        layouts=[
+            {
+                "relationship_type": "contains",
+                "child_templates": [
+                    {
+                        "template_code": "container/well/middle/1.0/",
+                        "json_addl": {"properties": {"name": "middle child"}},
+                    }
+                ],
+            }
+        ],
+    )
+    obj = _fake_bloom_obj([root, mid, leaf])
+
+    parent_rows, child_rows = obj.create_instances("TPL-ROOT")
+
+    assert len(parent_rows) == 1
+    assert len(child_rows) == 2
+    assert {child.type for child in child_rows} == {"well", "material"}
+    lineages = [
+        row for row in obj.session.added if isinstance(row, _FakeLineage)
+    ]
+    assert [row.relationship_type for row in lineages] == [
+        "HOLDS_MATERIAL",
+        "contains",
+    ]
+
+
+def test_create_instances_rejects_template_cycles():
+    cyclic = _template(
+        "TPL-CYCLE",
+        "container",
+        "plate",
+        "cycle",
+        layouts=[
+            {
+                "relationship_type": "contains",
+                "child_templates": [
+                    {"template_code": "container/plate/cycle/1.0/"}
+                ],
+            }
+        ],
+    )
+    obj = _fake_bloom_obj([cyclic])
+
+    with pytest.raises(Exception, match="cycle detected"):
+        obj.create_instances("TPL-CYCLE")
+
+
+def test_create_instances_enforces_object_count_limit():
+    child = _template("TPL-CHILD", "container", "well", "child")
+    root = _template(
+        "TPL-ROOT",
+        "container",
+        "plate",
+        "limited",
+        layouts=[
+            {
+                "relationship_type": "contains",
+                "child_templates": [
+                    {"template_code": "container/well/child/1.0/"}
+                ],
+            }
+        ],
+    )
+    obj = _fake_bloom_obj([root, child])
+
+    with pytest.raises(Exception, match="exceeded max object count 1"):
+        obj.create_instances("TPL-ROOT", max_instances=1)

From 62d060ccc5f6ad12c9f257ae6e9a024defd697af Mon Sep 17 00:00:00 2001
From: John Major 
Date: Thu, 18 Jun 2026 09:13:12 -0700
Subject: [PATCH 37/47] Refresh README current-state documentation

---
 README.md | 43 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 40 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 6f25162..51f11ea 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,50 @@
-# BLOOM
+
+
+██████╗ ██╗      ██████╗  ██████╗ ███╗   ███╗
+██╔══██╗██║     ██╔═══██╗██╔═══██╗████╗ ████║
+██████╔╝██║     ██║   ██║██║   ██║██╔████╔██║
+██╔══██╗██║     ██║   ██║██║   ██║██║╚██╔╝██║
+██████╔╝███████╗╚██████╔╝╚██████╔╝██║ ╚═╝ ██║
+╚═════╝ ╚══════╝ ╚═════╝  ╚═════╝ ╚═╝     ╚═╝
+
+

Internal material, container, lineage, and lab-action graph service.

+

+ Quickstart · + API · + GUI · + Lab actions · + Tests +

+
## Overview Bloom is the LSMC internal material and container graph service. It owns laboratory containers, materials/content, equipment, lineage, recursive template creation, search, and graph views. Atlas owns orders and customer-facing accession context; Bloom owns the physical/material execution graph. -Current Dayhoff pin: `7.0.14`. Current TapDB dependency: `daylily-tapdb @ ...@9.0.4`. +Current Dayhoff pin: `7.0.16`. Current TapDB dependency: `daylily-tapdb @ ...@9.0.5`. Bloom is internal-only in Dayhoff exposure policy. It must be reachable only through approved LSMC networks and Dayhoff-generated service credentials. +## What It Does + +- Owns Bloom containers, materials/content, equipment, recursive templates, lineages, search, and graph views. +- Exposes `/lab-actions` and `/api/v1/lab-actions/*` for extraction plate, sequencing-library plate, pool-tube, and sequencing-run setup. +- Mounts TapDB at `/tapdb` for generic object, template, lineage, audit, graph, and external-link inspection. +- Provides health and observability surfaces for Kahlo and Dayhoff deployment verification. + +## How It Works + +```mermaid +flowchart LR + Tube["Incoming tube + content"] --> Extraction["Extraction plate + gDNA"] + Extraction --> Library["Sequencing-library plate"] + Library --> Pool["Pool tube + pool content"] + Pool --> RunSet["Sequencing run set"] + RunSet --> OWY["Samplesheet for OWY/Kahlo traceability"] +``` + +Bloom models physical/material execution. Atlas owns orders and customer accession context; Dewey owns durable artifact identity; Ursa owns analysis trigger/job records. + ## Quickstart ```bash @@ -76,7 +113,7 @@ python -m pytest tests -q python -m pytest tests/test_lsmc_ui_skin_system_contract.py -q ``` -Deployed browser evidence should target `https://bloom..dev.lsmc.bio` and include the dashboard, object search/detail, TapDB mount, graph, and auth redirect surfaces. Current committed `jemdev5` evidence is linked from Dayhoff `docs/releases/dayhoff_7_0_61_jemdev5_evidence/`. +Deployed browser evidence should target `https://bloom..dev.lsmc.bio` and include the dashboard, object search/detail, TapDB mount, graph, and auth redirect surfaces. Current `jemdev5` deployment evidence is linked from Dayhoff `docs/plans/20260618T132900Z_jemdev5_dayhoff_7067_live_deploy_ledger.md`. ## Technical Details, History, And Linkouts From 8b64cad18e4c496be2fc46afee5a543f54ce0622 Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 18 Jun 2026 09:34:08 -0700 Subject: [PATCH 38/47] Add lab-action spreadsheet upload flows --- README.md | 10 +- bloom_lims/api/v1/lab_actions.py | 97 ++- bloom_lims/domain/lab_action_spreadsheets.py | 193 ++++++ bloom_lims/domain/lab_actions.py | 563 +++++++++++++++++- bloom_lims/schemas/lab_actions.py | 103 ++++ docs/apis.md | 6 + docs/gui.md | 4 + docs/lab_actions.md | 127 +++- ...om_lab_action_spreadsheet_upload_ledger.md | 75 +++ templates/modern/lab_actions.html | 151 ++++- tests/test_lab_actions_api.py | 303 ++++++++++ 11 files changed, 1615 insertions(+), 17 deletions(-) create mode 100644 bloom_lims/domain/lab_action_spreadsheets.py create mode 100644 docs/plans/20260618T190000Z_bloom_lab_action_spreadsheet_upload_ledger.md diff --git a/README.md b/README.md index 51f11ea..b337713 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ For normal user/admin behavior, the CLI, API, and GUI are alternate surfaces ove Bloom exposes a FastAPI/Jinja GUI for internal operators. Current surfaces include dashboard/home, object search and details, container/content/equipment operations, graph views, auth/profile flows, the `/lab-actions` wet-lab action wizard, and the mounted TapDB GUI at `/tapdb` when configured by Dayhoff. -`/lab-actions` is the temporary operator flow for mapping incoming biospecimen tubes to extraction plates, sequencing-library plates, pool tubes, and sequencing run sets. It is backed by `/api/v1/lab-actions/*`; GUI actions should not have behavior that is unavailable through the API. +`/lab-actions` is the temporary operator flow for mapping incoming biospecimen tubes to extraction plates, extraction QC plates, sequencing-library plates, pool tubes, generic lab/run sets, per-well associated data, and sequencing run sets. It also accepts `.csv` and `.xlsx` spreadsheet uploads for the same API-backed actions. GUI actions should not have behavior that is unavailable through the API. See [`docs/lab_actions.md`](docs/lab_actions.md) for the template matrix, lineage contract, API examples, GUI flow, and production rollout checklist for this surface. @@ -93,9 +93,15 @@ The primary API is under `/api/v1/*`. Current route families include objects, co The lab-action route family includes: - `POST /api/v1/lab-actions/extraction-plates` +- `POST /api/v1/lab-actions/extraction-qc-plates` - `POST /api/v1/lab-actions/seq-library-plates` - `POST /api/v1/lab-actions/seq-library-pools` - `POST /api/v1/lab-actions/seq-runs` +- `POST /api/v1/lab-actions/sets` +- `GET /api/v1/lab-actions/sets/{set_euid}` +- `POST /api/v1/lab-actions/sets/{set_euid}/members` +- `POST /api/v1/lab-actions/plate-well-data` +- `POST /api/v1/lab-actions/spreadsheet-import` - `GET /api/v1/lab-actions/seq-runs/{set_euid}/samplesheet` - `GET /api/v1/lab-actions/plates/{plate_euid}/mapping.csv` - `POST /api/v1/lab-actions/print-euids` @@ -119,7 +125,7 @@ Deployed browser evidence should target `https://bloom..dev.lsmc.bio` an - [`docs/apis.md`](docs/apis.md): API details. - [`docs/gui.md`](docs/gui.md): GUI routes and screenshots when current. -- [`docs/lab_actions.md`](docs/lab_actions.md): extraction/library/pooling/sequencing-run action flow, template matrix, and production rollout checklist. +- [`docs/lab_actions.md`](docs/lab_actions.md): extraction/QC/library/pooling/set/sequencing-run action flow, spreadsheet upload schemas, template matrix, and production rollout checklist. - [`docs/architecture.md`](docs/architecture.md): domain model and runtime boundaries. - [`docs/becoming_a_discoverable_service.md`](docs/becoming_a_discoverable_service.md): Dayhoff/Kahlo observability contract. - [`docs/plans/`](docs/plans/): active ledgers. diff --git a/bloom_lims/api/v1/lab_actions.py b/bloom_lims/api/v1/lab_actions.py index 0296c8a..591a32e 100644 --- a/bloom_lims/api/v1/lab_actions.py +++ b/bloom_lims/api/v1/lab_actions.py @@ -4,12 +4,16 @@ import logging -from fastapi import APIRouter, Depends, HTTPException +from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile from fastapi.responses import PlainTextResponse from bloom_lims.domain.lab_actions import LabActionsService from bloom_lims.schemas.lab_actions import ( ExtractionPlateRequest, + ExtractionQcPlateRequest, + LabSetMembersRequest, + LabSetRequest, + PlateWellDataRequest, PrintEuidRequest, SeqLibraryPlateRequest, SeqLibraryPoolRequest, @@ -53,6 +57,34 @@ async def create_extraction_plate( service.close() +@router.post("/extraction-qc-plates") +async def fill_extraction_qc_plate( + payload: ExtractionQcPlateRequest, + user: APIUser = Depends(require_write), +): + service = _service_for_user(user) + try: + return service.fill_extraction_qc_plate(payload) + except Exception as exc: + _raise_http(exc) + finally: + service.close() + + +@router.post("/plate-well-data") +async def attach_plate_well_data( + payload: PlateWellDataRequest, + user: APIUser = Depends(require_write), +): + service = _service_for_user(user) + try: + return service.attach_plate_well_data(payload) + except Exception as exc: + _raise_http(exc) + finally: + service.close() + + @router.post("/seq-library-plates") async def create_seq_library_plate( payload: SeqLibraryPlateRequest, @@ -95,6 +127,49 @@ async def create_seq_run_set( service.close() +@router.post("/sets") +async def create_lab_set( + payload: LabSetRequest, + user: APIUser = Depends(require_write), +): + service = _service_for_user(user) + try: + return service.create_lab_set(payload) + except Exception as exc: + _raise_http(exc) + finally: + service.close() + + +@router.get("/sets/{set_euid}") +async def get_lab_set( + set_euid: str, + user: APIUser = Depends(require_read), +): + service = _service_for_user(user) + try: + return service.get_lab_set(set_euid) + except Exception as exc: + _raise_http(exc) + finally: + service.close() + + +@router.post("/sets/{set_euid}/members") +async def add_lab_set_members( + set_euid: str, + payload: LabSetMembersRequest, + user: APIUser = Depends(require_write), +): + service = _service_for_user(user) + try: + return service.add_lab_set_members(set_euid, payload) + except Exception as exc: + _raise_http(exc) + finally: + service.close() + + @router.get("/seq-runs/{set_euid}/samplesheet") async def download_seq_run_sample_sheet( set_euid: str, @@ -137,6 +212,26 @@ async def download_plate_mapping_csv( service.close() +@router.post("/spreadsheet-import") +async def import_lab_action_spreadsheet( + file: UploadFile = File(...), + dry_run: bool = Query(True), + user: APIUser = Depends(require_write), +): + service = _service_for_user(user) + try: + data = await file.read() + return service.import_spreadsheet( + filename=file.filename or "upload.xlsx", + data=data, + dry_run=dry_run, + ) + except Exception as exc: + _raise_http(exc) + finally: + service.close() + + @router.post("/print-euids") async def print_euids( payload: PrintEuidRequest, diff --git a/bloom_lims/domain/lab_action_spreadsheets.py b/bloom_lims/domain/lab_action_spreadsheets.py new file mode 100644 index 0000000..e025cc9 --- /dev/null +++ b/bloom_lims/domain/lab_action_spreadsheets.py @@ -0,0 +1,193 @@ +"""Spreadsheet parsing helpers for Bloom lab-action uploads.""" + +from __future__ import annotations + +import csv +import io +import re +import zipfile +from dataclasses import dataclass +from pathlib import PurePosixPath +from typing import Any +from xml.etree import ElementTree + + +@dataclass(frozen=True) +class ParsedSheet: + name: str + headers: list[str] + rows: list[dict[str, Any]] + + +_NS = { + "main": "http://schemas.openxmlformats.org/spreadsheetml/2006/main", + "rel": "http://schemas.openxmlformats.org/officeDocument/2006/relationships", + "pkgrel": "http://schemas.openxmlformats.org/package/2006/relationships", +} + + +def normalize_header(value: Any) -> str: + """Normalize operator spreadsheet headers to stable snake_case keys.""" + text = str(value or "").strip().lower() + replacements = { + "continer": "container", + "conteiner": "container", + "templae": "template", + "destination": "target", + "(r)": "", + "(data)": "", + } + for bad, good in replacements.items(): + text = text.replace(bad, good) + text = text.replace("/", " ") + text = re.sub(r"[^a-z0-9]+", "_", text) + text = re.sub(r"_+", "_", text).strip("_") + return text + + +def _cell_column_index(cell_ref: str) -> int: + match = re.match(r"([A-Z]+)", cell_ref.upper()) + if not match: + return 0 + value = 0 + for char in match.group(1): + value = value * 26 + (ord(char) - ord("A") + 1) + return value - 1 + + +def _xml_text(element: ElementTree.Element | None) -> str: + if element is None: + return "" + return "".join(element.itertext()) + + +def _shared_strings(archive: zipfile.ZipFile) -> list[str]: + try: + root = ElementTree.fromstring(archive.read("xl/sharedStrings.xml")) + except KeyError: + return [] + strings: list[str] = [] + for item in root.findall("main:si", _NS): + strings.append(_xml_text(item)) + return strings + + +def _sheet_paths(archive: zipfile.ZipFile) -> list[tuple[str, str]]: + workbook = ElementTree.fromstring(archive.read("xl/workbook.xml")) + rels = ElementTree.fromstring(archive.read("xl/_rels/workbook.xml.rels")) + rel_by_id = { + rel.attrib["Id"]: rel.attrib["Target"] + for rel in rels.findall("pkgrel:Relationship", _NS) + } + sheets: list[tuple[str, str]] = [] + for sheet in workbook.findall("main:sheets/main:sheet", _NS): + rel_id = sheet.attrib.get(f"{{{_NS['rel']}}}id", "") + target = rel_by_id.get(rel_id) + if not target: + continue + clean_target = target.lstrip("/") + path = ( + PurePosixPath(clean_target) + if clean_target.startswith("xl/") + else PurePosixPath("xl") / clean_target + ) + sheets.append((sheet.attrib.get("name", "Sheet"), str(path))) + return sheets + + +def _parse_xlsx_sheet( + archive: zipfile.ZipFile, path: str, shared_strings: list[str] +) -> list[list[Any]]: + root = ElementTree.fromstring(archive.read(path)) + rows: list[list[Any]] = [] + for row_el in root.findall(".//main:sheetData/main:row", _NS): + values: list[Any] = [] + for cell in row_el.findall("main:c", _NS): + col_index = _cell_column_index(cell.attrib.get("r", "A")) + while len(values) <= col_index: + values.append(None) + cell_type = cell.attrib.get("t") + value_el = cell.find("main:v", _NS) + if cell_type == "s": + raw = _xml_text(value_el) + values[col_index] = ( + shared_strings[int(raw)] if raw and raw.isdigit() else raw + ) + elif cell_type == "inlineStr": + values[col_index] = _xml_text(cell.find("main:is", _NS)) + else: + raw = _xml_text(value_el) + values[col_index] = raw if raw != "" else None + rows.append(values) + return rows + + +def _find_header_row(rows: list[list[Any]]) -> int | None: + best_index = None + best_score = 0 + for index, row in enumerate(rows[:20]): + headers = [normalize_header(value) for value in row] + non_empty = [header for header in headers if header] + score = len(set(non_empty)) + if any( + token in set(non_empty) + for token in { + "tube_euid", + "container_euid", + "source_well_euid", + "pool_tube_euid", + "annotation_template_euid", + } + ): + score += 10 + if score > best_score: + best_score = score + best_index = index + return best_index if best_score >= 2 else None + + +def _rows_to_sheet(name: str, rows: list[list[Any]]) -> ParsedSheet: + header_index = _find_header_row(rows) + if header_index is None: + return ParsedSheet(name=name, headers=[], rows=[]) + headers = [normalize_header(value) for value in rows[header_index]] + seen: dict[str, int] = {} + unique_headers: list[str] = [] + for index, header in enumerate(headers): + if not header: + header = f"unnamed_{index + 1}" + count = seen.get(header, 0) + seen[header] = count + 1 + unique_headers.append(header if count == 0 else f"{header}_{count + 1}") + parsed_rows: list[dict[str, Any]] = [] + for row in rows[header_index + 1 :]: + item = { + header: row[index] if index < len(row) else None + for index, header in enumerate(unique_headers) + } + if any(value not in (None, "") for value in item.values()): + parsed_rows.append(item) + return ParsedSheet(name=name, headers=unique_headers, rows=parsed_rows) + + +def parse_workbook(filename: str, data: bytes) -> list[ParsedSheet]: + """Parse CSV/XLSX data into normalized sheets. + + This function intentionally avoids optional Excel dependencies so deployed + Bloom containers can accept simple operator workbooks without changing the + runtime package set. + """ + suffix = filename.rsplit(".", 1)[-1].lower() if "." in filename else "" + if suffix == "csv": + text = data.decode("utf-8-sig") + reader = csv.reader(io.StringIO(text)) + return [_rows_to_sheet("CSV", list(reader))] + if suffix != "xlsx": + raise ValueError("Spreadsheet upload must be .csv or .xlsx") + sheets: list[ParsedSheet] = [] + with zipfile.ZipFile(io.BytesIO(data)) as archive: + shared_strings = _shared_strings(archive) + for sheet_name, sheet_path in _sheet_paths(archive): + rows = _parse_xlsx_sheet(archive, sheet_path, shared_strings) + sheets.append(_rows_to_sheet(sheet_name, rows)) + return sheets diff --git a/bloom_lims/domain/lab_actions.py b/bloom_lims/domain/lab_actions.py index 6f4588c..6579bc1 100644 --- a/bloom_lims/domain/lab_actions.py +++ b/bloom_lims/domain/lab_actions.py @@ -5,18 +5,26 @@ import csv import io import json +import re from dataclasses import dataclass from typing import Any from sqlalchemy.orm.attributes import flag_modified from bloom_lims.domain.base import BloomObj +from bloom_lims.domain.lab_action_spreadsheets import ParsedSheet, parse_workbook from bloom_lims.domain.v0_graph import attach_bloom_v0_edge, object_evidence from bloom_lims.integrations.zebra_day import ZebraDayService from bloom_lims.schemas.lab_actions import ( ExtractionPlateRequest, + ExtractionQcAssignment, + ExtractionQcPlateRequest, ExtractionTubeAssignment, + LabSetMembersRequest, + LabSetRequest, LibraryPlateAssignment, + PlateWellDataRecord, + PlateWellDataRequest, PrintEuidRequest, RunSetInput, SeqLibraryPlateRequest, @@ -37,6 +45,11 @@ POOL_TUBE_TEMPLATE_CODE = "container/tube/tube-generic-10ml/1.0/" POOL_CONTENT_TEMPLATE_CODE = "content/pool/sequencing-library/1.0/" GDNA_QUANT_TEMPLATE_CODE = "data/quantification/gdna/1.0/" +EXTRACTION_QC_TEMPLATE_CODE = "data/operation/extraction-qc/1.0/" + + +def re_split_multi(value: str) -> list[str]: + return re.split(r"[\n,|;]+", value) @dataclass(frozen=True) @@ -256,9 +269,9 @@ def _run_set( "members": members, "external_members": payload.external_members, "metadata": payload.metadata, - "status": "created", + "status": payload.status, }, - status="created", + status=payload.status, ) for member in members: if self.bobj.get_by_euid(member) is None: @@ -267,6 +280,233 @@ def _run_set( self._create_lineage(member, instance.euid, "associated_set") return instance + def _well_from_plate_position(self, plate_euid: str, row: str, col: int): + plate = self._require(plate_euid) + wells = self._plate_wells(plate) + position = WellPosition(row=row, col=col).name + well = wells.get(position) + if well is None: + raise ValueError(f"Destination well not found on plate: {position}") + return well + + def _well_for_data_record(self, record: PlateWellDataRecord): + if record.well_euid: + return self._require(record.well_euid) + return self._well_from_plate_position(record.plate_euid, record.row, record.col) + + def _well_for_qc_assignment( + self, assignment: ExtractionQcAssignment, source_plate_euid: str | None + ): + if assignment.source_well_euid: + return self._require(assignment.source_well_euid) + if not source_plate_euid: + raise ValueError("source_plate_euid is required when assignment uses row/col") + return self._well_from_plate_position( + source_plate_euid, assignment.row, assignment.col + ) + + def _create_or_get_qc_plate(self, request: ExtractionQcPlateRequest): + if request.qc_plate_euid: + plate = self._require(request.qc_plate_euid) + if instance_semantic_category(plate) != "container" or plate.type != "plate": + raise ValueError(f"{request.qc_plate_euid} is not a plate container") + return plate, [] + plate = self._create_by_code( + EXTRACTION_PLATE_TEMPLATE_CODE, + name=request.qc_plate_name or "extraction QC plate", + ) + return plate, [plate.euid] + + def _all_filled_well_qc_assignments( + self, source_plate_euid: str + ) -> list[ExtractionQcAssignment]: + plate = self._require(source_plate_euid) + assignments: list[ExtractionQcAssignment] = [] + for name, well in sorted(self._plate_wells(plate).items()): + if self._lineages_from_parent(well, "HOLDS_MATERIAL"): + assignments.append( + ExtractionQcAssignment( + source_well_euid=well.euid, + qc_row=name[:1], + qc_col=int(name[1:]), + ) + ) + if not assignments: + raise ValueError(f"Source plate {source_plate_euid} has no filled wells") + return assignments + + def create_lab_set(self, request: LabSetRequest) -> dict[str, Any]: + run_set = self._create_by_code( + RUN_SET_TEMPLATE_CODE, + name=request.name, + properties={ + "name": request.name, + "description": request.description or "", + "members": list(dict.fromkeys(request.members)), + "external_members": list(dict.fromkeys(request.external_members)), + "operator": request.operator or "", + "instrument": request.instrument or "", + "reagents": request.reagents, + "machine": request.machine or "", + "flowcell_barcode": request.flowcell_barcode or "", + "status": request.status, + "metadata": request.metadata, + }, + status=request.status, + ) + for member in dict.fromkeys(request.members): + if self.bobj.get_by_euid(member) is not None: + self._create_lineage(run_set.euid, member, "run_set_member") + self._create_lineage(member, run_set.euid, "associated_set") + self.bdb.session.commit() + return self.get_lab_set(run_set.euid) + + def add_lab_set_members( + self, set_euid: str, request: LabSetMembersRequest + ) -> dict[str, Any]: + run_set = self._require(set_euid) + props = self._props(run_set) + members = list( + dict.fromkeys([*props.get("members", []), *request.members]) + ) + external_members = list( + dict.fromkeys( + [*props.get("external_members", []), *request.external_members] + ) + ) + props["members"] = members + props["external_members"] = external_members + self._write_props(run_set, props) + for member in request.members: + if self.bobj.get_by_euid(member) is not None: + self._create_lineage(run_set.euid, member, "run_set_member") + self._create_lineage(member, run_set.euid, "associated_set") + self.bdb.session.commit() + return self.get_lab_set(set_euid) + + def get_lab_set(self, set_euid: str) -> dict[str, Any]: + run_set = self._require(set_euid) + props = self._props(run_set) + return { + "set_euid": run_set.euid, + "name": run_set.name, + "status": run_set.bstatus, + "properties": props, + "members": props.get("members", []), + "external_members": props.get("external_members", []), + } + + def fill_extraction_qc_plate( + self, request: ExtractionQcPlateRequest + ) -> dict[str, Any]: + assignments = request.assignments or self._all_filled_well_qc_assignments( + request.source_plate_euid + ) + if len(assignments) > 96: + raise ValueError("extraction QC plate can accept at most 96 assignments") + qc_plate, new_container_euids = self._create_or_get_qc_plate(request) + qc_wells = self._plate_wells(qc_plate) + used_qc_positions: set[str] = set() + mappings: list[dict[str, Any]] = [] + for index, assignment in enumerate(assignments): + source_well = self._well_for_qc_assignment( + assignment, request.source_plate_euid + ) + source_content = self._well_content(source_well) + if assignment.qc_row is not None and assignment.qc_col is not None: + qc_position = WellPosition( + row=assignment.qc_row, col=assignment.qc_col + ).name + else: + qc_position = WellPosition( + row=assignment.row or ROWS[index // 12], + col=assignment.col or (index % 12) + 1, + ).name + if qc_position in used_qc_positions: + raise ValueError(f"duplicate QC destination well: {qc_position}") + used_qc_positions.add(qc_position) + qc_well = qc_wells.get(qc_position) + if qc_well is None: + raise ValueError(f"QC well not found on plate: {qc_position}") + data = self._create_by_code( + EXTRACTION_QC_TEMPLATE_CODE, + name=f"{source_well.euid} extraction QC", + properties={ + "source_well_euid": source_well.euid, + "source_content_euid": source_content.euid, + "qc_plate_euid": qc_plate.euid, + "qc_well_euid": qc_well.euid, + "qc_well_name": qc_position, + "result": assignment.result or "", + "status": assignment.status or "recorded", + "data": assignment.data, + }, + status=assignment.status or "recorded", + ) + self._create_lineage(source_well.euid, qc_well.euid, "qc_source_well") + self._create_lineage(qc_well.euid, data.euid, "well_associated_data") + self._create_lineage(source_content.euid, data.euid, "qc_for_material") + mappings.append( + { + "source_well_euid": source_well.euid, + "source_content_euid": source_content.euid, + "qc_well_euid": qc_well.euid, + "qc_well_name": qc_position, + "data_euid": data.euid, + "result": assignment.result or "", + "status": assignment.status or "recorded", + } + ) + run_set = self._run_set( + requested=request.create_run_set, + run_set=request.run_set, + default_name=f"{qc_plate.euid} extraction QC set", + member_euids=[qc_plate.euid, *[item["data_euid"] for item in mappings]], + ) + self.bdb.session.commit() + return { + "qc_plate_euid": qc_plate.euid, + "run_set_euid": run_set.euid if run_set is not None else None, + "new_container_euids": new_container_euids, + "mappings": mappings, + } + + def attach_plate_well_data(self, request: PlateWellDataRequest) -> dict[str, Any]: + mappings: list[dict[str, Any]] = [] + for record in request.records: + well = self._well_for_data_record(record) + target = well if record.target == "well" else self._well_content(well) + data = self._create_by_code( + request.data_template_code, + name=record.name or f"{well.euid} associated data", + properties={ + "well_euid": well.euid, + "target_euid": target.euid, + "target_kind": record.target, + "data": record.data, + }, + ) + self._create_lineage(target.euid, data.euid, request.relationship_type) + mappings.append( + { + "well_euid": well.euid, + "target_euid": target.euid, + "data_euid": data.euid, + "relationship_type": request.relationship_type, + } + ) + run_set = self._run_set( + requested=request.create_run_set, + run_set=request.run_set, + default_name="plate well data set", + member_euids=[item["data_euid"] for item in mappings], + ) + self.bdb.session.commit() + return { + "run_set_euid": run_set.euid if run_set is not None else None, + "mappings": mappings, + } + def create_extraction_plate(self, request: ExtractionPlateRequest) -> dict[str, Any]: assignments = self._normalize_assignments(request) plate = self._create_or_get_extraction_plate(request) @@ -689,6 +929,325 @@ def illumina_sample_sheet(self, set_euid: str) -> str: writer.writerows(data_rows) return output.getvalue() + def _split_values(self, value: Any) -> list[str]: + if value in (None, ""): + return [] + return [ + token.strip() + for token in re_split_multi(str(value)) + if token and token.strip() + ] + + def _row_metadata(self, row: dict[str, Any], *prefixes: str) -> dict[str, Any]: + metadata: dict[str, Any] = {} + for key, value in row.items(): + if value in (None, ""): + continue + for prefix in prefixes: + if key.startswith(prefix): + metadata[key.removeprefix(prefix)] = value + return metadata + + def _sheet_action(self, sheet: ParsedSheet) -> str: + name = sheet.name.strip().lower().replace("-", " ").replace("_", " ") + headers = set(sheet.headers) + if name == "csv": + if {"pool_tube_euid", "flowcell_barcode"} & headers: + return "seq_run_set" + if {"input_euid", "input_euids"} & headers: + return "seq_pool" + if "source_well_euid" in headers and {"index_barcode", "index_euid"} & headers: + return "seq_library_plate" + if {"source_well_euid", "qc_row", "qc_col"} & headers: + return "extraction_qc" + if {"well_euid", "data_template_code"} & headers: + return "plate_well_data" + if {"set_name", "members", "member_euids"} & headers: + return "sets" + if "tube_euid" in headers: + return "extraction_plate" + if name in {"extraction plate", "extraction"}: + return "extraction_plate" + if name in {"extraction qc", "extractionqc", "extraction qc plate"}: + return "extraction_qc" + if name in {"seq library plate", "sequencing library plate", "library plate"}: + return "seq_library_plate" + if name in {"seq pool", "sequencing pool", "seq library pool", "pool"}: + return "seq_pool" + if name in {"seq run", "seq run set", "sequencing run", "sequencing run set"}: + return "seq_run_set" + if name in {"sets", "set", "run sets", "run set"}: + return "sets" + if name in {"plate well data", "well data", "annotation"}: + return "plate_well_data" if name != "annotation" else "container_annotation_preview" + if name in {"bulk create", "transfer"}: + return "container_interaction_preview" + return "unknown" + + def _request_from_extraction_sheet(self, sheet: ParsedSheet) -> ExtractionPlateRequest: + assignments: list[ExtractionTubeAssignment] = [] + tube_euids: list[str] = [] + plate_name: str | None = None + for row in sheet.rows: + tube_euid = str( + row.get("tube_euid") or row.get("container_euid") or "" + ).strip() + if not tube_euid: + continue + plate_name = plate_name or str(row.get("plate_name") or "").strip() or None + row_name = row.get("row") or row.get("well_row") or row.get("child_container_row") + col_value = row.get("col") or row.get("well_col") or row.get("child_container_column") + if row_name not in (None, "") or col_value not in (None, ""): + assignments.append( + ExtractionTubeAssignment( + tube_euid=tube_euid, + row=row_name, + col=col_value, + quant=self._row_metadata(row, "quant_", "qc_"), + ) + ) + else: + tube_euids.append(tube_euid) + if assignments: + return ExtractionPlateRequest( + mode="directed", + assignments=assignments, + plate_name=plate_name or "extraction plate", + ) + return ExtractionPlateRequest( + mode="auto", + tube_euids=tube_euids, + plate_name=plate_name or "extraction plate", + ) + + def _request_from_qc_sheet(self, sheet: ParsedSheet) -> ExtractionQcPlateRequest: + source_plate_euid = None + qc_plate_name = None + assignments: list[ExtractionQcAssignment] = [] + for row in sheet.rows: + source_plate_euid = source_plate_euid or str( + row.get("source_plate_euid") or row.get("plate_euid") or "" + ).strip() or None + qc_plate_name = qc_plate_name or str(row.get("qc_plate_name") or "").strip() or None + assignments.append( + ExtractionQcAssignment( + source_well_euid=str(row.get("source_well_euid") or row.get("well_euid") or "").strip() + or None, + row=row.get("row") or row.get("well_row"), + col=row.get("col") or row.get("well_col"), + qc_row=row.get("qc_row") or row.get("target_row"), + qc_col=row.get("qc_col") or row.get("target_col"), + result=str(row.get("result") or "").strip() or None, + status=str(row.get("status") or "").strip() or None, + data=self._row_metadata(row, "data_", "qc_", "metric_"), + ) + ) + return ExtractionQcPlateRequest( + source_plate_euid=source_plate_euid, + qc_plate_name=qc_plate_name or "extraction QC plate", + assignments=assignments, + ) + + def _request_from_library_sheet(self, sheet: ParsedSheet) -> SeqLibraryPlateRequest: + source_plate_euid = None + plate_name = None + assignments: list[LibraryPlateAssignment] = [] + for row in sheet.rows: + source_plate_euid = source_plate_euid or str( + row.get("source_plate_euid") or "" + ).strip() or None + plate_name = plate_name or str(row.get("plate_name") or "").strip() or None + source_well_euid = str(row.get("source_well_euid") or row.get("well_euid") or "").strip() + if source_well_euid: + assignments.append( + LibraryPlateAssignment( + source_well_euid=source_well_euid, + row=row.get("row") or row.get("well_row"), + col=row.get("col") or row.get("well_col"), + index_barcode=str(row.get("index_barcode") or "").strip() or None, + index_euid=str(row.get("index_euid") or "").strip() or None, + data=self._row_metadata(row, "data_", "library_"), + ) + ) + if assignments: + return SeqLibraryPlateRequest( + mode="directed", + assignments=assignments, + plate_name=plate_name or "seq library plate", + ) + return SeqLibraryPlateRequest( + mode="plate_1_to_1", + source_plate_euid=source_plate_euid, + plate_name=plate_name or "seq library plate", + ) + + def _request_from_pool_sheet(self, sheet: ParsedSheet) -> SeqLibraryPoolRequest: + input_euids: list[str] = [] + platform = "ILMN" + pool_tube_euid = None + pool_name = None + metadata: dict[str, Any] = {} + for row in sheet.rows: + input_euids.extend( + self._split_values( + row.get("input_euid") + or row.get("input_euids") + or row.get("well_euid") + or row.get("tube_euid") + or row.get("container_euid") + or row.get("content_euid") + ) + ) + platform = str(row.get("platform") or platform).strip() or platform + pool_tube_euid = pool_tube_euid or str(row.get("pool_tube_euid") or "").strip() or None + pool_name = pool_name or str(row.get("pool_name") or "").strip() or None + metadata.update(self._row_metadata(row, "metadata_", "pool_")) + return SeqLibraryPoolRequest( + input_euids=list(dict.fromkeys(input_euids)), + platform=platform, + pool_tube_euid=pool_tube_euid, + pool_name=pool_name or "seq library", + metadata=metadata, + ) + + def _request_from_run_sheet(self, sheet: ParsedSheet) -> SeqRunSetRequest: + if not sheet.rows: + raise ValueError("Seq Run Set sheet has no rows") + row = sheet.rows[0] + return SeqRunSetRequest( + pool_tube_euid=str(row.get("pool_tube_euid") or "").strip(), + pool_content_euid=str(row.get("pool_content_euid") or "").strip() or None, + platform=str(row.get("platform") or "ILMN").strip() or "ILMN", + operator=str(row.get("operator") or "").strip() or None, + instrument_euid=str(row.get("instrument_euid") or "").strip() or None, + machine=str(row.get("machine") or "").strip() or None, + flowcell_barcode=str(row.get("flowcell_barcode") or row.get("flowcell") or "").strip(), + reagent_euids=self._split_values(row.get("reagent_euids") or row.get("reagents")), + status=str(row.get("status") or "created").strip() or "created", + name=str(row.get("name") or "").strip() or None, + description=str(row.get("description") or "").strip() or None, + metadata=self._row_metadata(row, "metadata_", "run_"), + ) + + def _request_from_data_sheet(self, sheet: ParsedSheet) -> PlateWellDataRequest: + records: list[PlateWellDataRecord] = [] + data_template_code = "data/operation/extraction-qc/1.0/" + relationship_type = "well_associated_data" + for row in sheet.rows: + data_template_code = str( + row.get("data_template_code") + or row.get("annotation_template_euid") + or data_template_code + ).strip() + relationship_type = str(row.get("relationship_type") or relationship_type).strip() + records.append( + PlateWellDataRecord( + well_euid=str(row.get("well_euid") or row.get("child_container_euid") or "").strip() + or None, + plate_euid=str(row.get("plate_euid") or row.get("container_euid") or "").strip() + or None, + row=row.get("row") or row.get("well_row") or row.get("child_container_row"), + col=row.get("col") or row.get("well_col") or row.get("child_container_column"), + name=str(row.get("name") or row.get("annotation_name") or "").strip() + or None, + target="content" + if "content" + in str( + row.get("target") + or row.get("annotate_container_child_container_content") + or "content" + ).strip().lower() + else "well", + data={ + **self._row_metadata(row, "data_", "annotation_", "metric_"), + **({"value": row.get("annotation_value")} if row.get("annotation_value") not in (None, "") else {}), + }, + ) + ) + return PlateWellDataRequest( + data_template_code=data_template_code, + relationship_type=relationship_type, + records=records, + ) + + def _requests_from_sets_sheet(self, sheet: ParsedSheet) -> list[LabSetRequest]: + requests: list[LabSetRequest] = [] + for row in sheet.rows: + name = str(row.get("name") or row.get("set_name") or "").strip() + if not name: + continue + requests.append( + LabSetRequest( + name=name, + description=str(row.get("description") or "").strip() or None, + members=self._split_values(row.get("members") or row.get("member_euids")), + external_members=self._split_values(row.get("external_members")), + operator=str(row.get("operator") or "").strip() or None, + instrument=str(row.get("instrument") or row.get("instrument_euid") or "").strip() or None, + reagents=self._split_values(row.get("reagents") or row.get("reagent_euids")), + machine=str(row.get("machine") or "").strip() or None, + flowcell_barcode=str(row.get("flowcell_barcode") or "").strip() or None, + status=str(row.get("status") or "created").strip() or "created", + metadata=self._row_metadata(row, "metadata_", "set_"), + ) + ) + return requests + + def import_spreadsheet( + self, *, filename: str, data: bytes, dry_run: bool = True + ) -> dict[str, Any]: + sheets = parse_workbook(filename, data) + actions: list[dict[str, Any]] = [] + for sheet in sheets: + action = self._sheet_action(sheet) + entry: dict[str, Any] = { + "sheet": sheet.name, + "action": action, + "headers": sheet.headers, + "row_count": len(sheet.rows), + } + if action in {"container_interaction_preview", "container_annotation_preview", "unknown"}: + entry["preview_rows"] = sheet.rows[:25] + actions.append(entry) + continue + if action == "extraction_plate": + request = self._request_from_extraction_sheet(sheet) + entry["request"] = request.model_dump(mode="json") + if not dry_run: + entry["result"] = self.create_extraction_plate(request) + elif action == "extraction_qc": + request = self._request_from_qc_sheet(sheet) + entry["request"] = request.model_dump(mode="json") + if not dry_run: + entry["result"] = self.fill_extraction_qc_plate(request) + elif action == "seq_library_plate": + request = self._request_from_library_sheet(sheet) + entry["request"] = request.model_dump(mode="json") + if not dry_run: + entry["result"] = self.create_seq_library_plate(request) + elif action == "seq_pool": + request = self._request_from_pool_sheet(sheet) + entry["request"] = request.model_dump(mode="json") + if not dry_run: + entry["result"] = self.create_seq_library_pool(request) + elif action == "seq_run_set": + request = self._request_from_run_sheet(sheet) + entry["request"] = request.model_dump(mode="json") + if not dry_run: + entry["result"] = self.create_seq_run_set(request) + elif action == "plate_well_data": + request = self._request_from_data_sheet(sheet) + entry["request"] = request.model_dump(mode="json") + if not dry_run: + entry["result"] = self.attach_plate_well_data(request) + elif action == "sets": + requests = self._requests_from_sets_sheet(sheet) + entry["request"] = [item.model_dump(mode="json") for item in requests] + if not dry_run: + entry["result"] = [self.create_lab_set(item) for item in requests] + actions.append(entry) + return {"filename": filename, "dry_run": dry_run, "actions": actions} + def print_euids(self, request: PrintEuidRequest) -> dict[str, Any]: service = ZebraDayService() results = [] diff --git a/bloom_lims/schemas/lab_actions.py b/bloom_lims/schemas/lab_actions.py index 6900f07..73c281c 100644 --- a/bloom_lims/schemas/lab_actions.py +++ b/bloom_lims/schemas/lab_actions.py @@ -56,6 +56,7 @@ class RunSetInput(BaseModel): members: list[str] = Field(default_factory=list) external_members: list[str] = Field(default_factory=list) metadata: dict[str, Any] = Field(default_factory=dict) + status: RunSetStatus = "created" class ExtractionPlateRequest(BaseModel): @@ -118,6 +119,83 @@ def validate_input(self) -> "SeqLibraryPlateRequest": return self +class ExtractionQcAssignment(BaseModel): + source_well_euid: str | None = None + row: str | None = None + col: int | None = None + qc_row: str | None = None + qc_col: int | None = None + result: str | None = None + status: str | None = None + data: dict[str, Any] = Field(default_factory=dict) + + @model_validator(mode="after") + def validate_source_position(self) -> "ExtractionQcAssignment": + if self.source_well_euid and (self.row is not None or self.col is not None): + raise ValueError("provide source_well_euid or row/col, not both") + if not self.source_well_euid and (self.row is None or self.col is None): + raise ValueError("source_well_euid or row/col is required") + if (self.row is None) ^ (self.col is None): + raise ValueError("row and col must be provided together") + if self.row is not None and self.col is not None: + WellPosition(row=self.row, col=self.col) + if (self.qc_row is None) ^ (self.qc_col is None): + raise ValueError("qc_row and qc_col must be provided together") + if self.qc_row is not None and self.qc_col is not None: + WellPosition(row=self.qc_row, col=self.qc_col) + return self + + +class ExtractionQcPlateRequest(BaseModel): + source_plate_euid: str | None = None + qc_plate_euid: str | None = None + qc_plate_name: str | None = None + assignments: list[ExtractionQcAssignment] = Field(default_factory=list) + create_run_set: bool = False + run_set: RunSetInput | None = None + + @model_validator(mode="after") + def validate_input(self) -> "ExtractionQcPlateRequest": + if not self.source_plate_euid and not self.assignments: + raise ValueError("source_plate_euid or assignments is required") + return self + + +class PlateWellDataRecord(BaseModel): + well_euid: str | None = None + plate_euid: str | None = None + row: str | None = None + col: int | None = None + name: str | None = None + target: Literal["well", "content"] = "content" + data: dict[str, Any] = Field(default_factory=dict) + + @model_validator(mode="after") + def validate_target(self) -> "PlateWellDataRecord": + if self.well_euid and (self.plate_euid or self.row is not None or self.col is not None): + raise ValueError("provide well_euid or plate_euid with row/col, not both") + if not self.well_euid: + if not self.plate_euid or self.row is None or self.col is None: + raise ValueError("well_euid or plate_euid with row/col is required") + WellPosition(row=self.row, col=self.col) + return self + + +class PlateWellDataRequest(BaseModel): + data_template_code: str = "data/operation/extraction-qc/1.0/" + relationship_type: str = "well_associated_data" + records: list[PlateWellDataRecord] + create_run_set: bool = False + run_set: RunSetInput | None = None + + @field_validator("records") + @classmethod + def validate_records(cls, value): + if not value: + raise ValueError("records must not be empty") + return value + + class SeqLibraryPoolRequest(BaseModel): input_euids: list[str] platform: PlatformName = "ILMN" @@ -151,6 +229,31 @@ class SeqRunSetRequest(BaseModel): metadata: dict[str, Any] = Field(default_factory=dict) +class LabSetRequest(BaseModel): + name: str + description: str | None = None + members: list[str] = Field(default_factory=list) + external_members: list[str] = Field(default_factory=list) + operator: str | None = None + instrument: str | None = None + reagents: list[str] = Field(default_factory=list) + machine: str | None = None + flowcell_barcode: str | None = None + status: RunSetStatus = "created" + metadata: dict[str, Any] = Field(default_factory=dict) + + +class LabSetMembersRequest(BaseModel): + members: list[str] = Field(default_factory=list) + external_members: list[str] = Field(default_factory=list) + + @model_validator(mode="after") + def validate_members(self) -> "LabSetMembersRequest": + if not self.members and not self.external_members: + raise ValueError("members or external_members is required") + return self + + class PrintEuidRequest(BaseModel): euids: list[str] lab: str diff --git a/docs/apis.md b/docs/apis.md index 55e89d5..f283ea6 100644 --- a/docs/apis.md +++ b/docs/apis.md @@ -370,9 +370,15 @@ The detailed operator/API runbook is [`lab_actions.md`](lab_actions.md). It is t Current routes: - `POST /api/v1/lab-actions/extraction-plates`: create or fill extraction plates from incoming filled tube EUIDs, create gDNA contents, and link tube/content/well/material provenance. +- `POST /api/v1/lab-actions/extraction-qc-plates`: create or fill extraction QC plates from extraction wells and attach QC data records. - `POST /api/v1/lab-actions/seq-library-plates`: create sequencing-library plates from filled extraction wells or directed mappings. - `POST /api/v1/lab-actions/seq-library-pools`: create or fill sequencing-library pool tubes from tube, well, content, or existing pool inputs. - `POST /api/v1/lab-actions/seq-runs`: create sequencing run sets with pool, instrument/operator/reagent metadata, flowcell barcode, platform, and status. +- `POST /api/v1/lab-actions/sets`: create generic lab/run sets for batches, reagents, instruments, operators, or external members. +- `GET /api/v1/lab-actions/sets/{set_euid}`: read the stored set metadata and members. +- `POST /api/v1/lab-actions/sets/{set_euid}/members`: append internal Bloom EUID members or external member identifiers. +- `POST /api/v1/lab-actions/plate-well-data`: attach structured data records to plate wells or well contents. +- `POST /api/v1/lab-actions/spreadsheet-import`: dry-run or execute `.csv`/`.xlsx` uploads for explicit lab-action sheet shapes. - `GET /api/v1/lab-actions/seq-runs/{set_euid}/samplesheet`: download ILMN sample sheets. Non-ILMN platforms fail explicitly until file formats are specified. - `GET /api/v1/lab-actions/plates/{plate_euid}/mapping.csv`: export one-degree parent/child plate mapping CSV. - `POST /api/v1/lab-actions/print-euids`: dispatch EUID barcode print requests through Zebra Day. diff --git a/docs/gui.md b/docs/gui.md index b0ebee2..e4be089 100644 --- a/docs/gui.md +++ b/docs/gui.md @@ -207,9 +207,13 @@ Older workflow/workset-centric creation surfaces are not the current product dir `/lab-actions` exposes the temporary wet-lab action wizard for: - incoming tube to extraction plate mapping +- extraction plate to extraction QC plate mapping - extraction plate to sequencing-library plate mapping - sequencing-library pool tube creation - sequencing run set setup +- generic lab/run set creation and member management +- plate-well associated data attachment +- spreadsheet upload for `.csv` and `.xlsx` lab-action sheets - plate CSV export - ILMN sample-sheet download - generic EUID barcode print dispatch diff --git a/docs/lab_actions.md b/docs/lab_actions.md index 21ec10a..3b5ec61 100644 --- a/docs/lab_actions.md +++ b/docs/lab_actions.md @@ -89,7 +89,34 @@ Directed mode lets the caller specify destination wells. It can create a new pla The action creates gDNA content in each destination well, links the tube to the well, and links the incoming tube content to the new gDNA content. -### 2. Sequencing-Library Plate +### 2. Extraction QC Plate + +Extraction QC fills a new or existing QC plate from an extraction plate. If assignments are omitted, Bloom uses every filled source well from the source extraction plate in row-major order. Explicit assignments can name source wells, destination QC wells, result/status, and structured QC data. + +```bash +curl -sS -X POST "$BLOOM_URL/api/v1/lab-actions/extraction-qc-plates" \ + -H "Authorization: Bearer $BLOOM_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "source_plate_euid": "Z-BCP-EXTRACTION", + "qc_plate_name": "extraction QC plate", + "assignments": [ + { + "source_well_euid": "Z-BCW-SOURCEA1", + "qc_row": "A", + "qc_col": 1, + "result": "pass", + "status": "recorded", + "data": {"concentration_ng_ul": 43.2, "a260_280": 1.83} + } + ], + "create_run_set": true + }' +``` + +The action creates `data/operation/extraction-qc/1.0/` records, links source well/content to QC well/data, and rejects requests above the 96-well plate capacity. + +### 3. Sequencing-Library Plate `plate_1_to_1` mode maps every filled extraction well to the same position on a new sequencing-library plate. @@ -106,7 +133,7 @@ curl -sS -X POST "$BLOOM_URL/api/v1/lab-actions/seq-library-plates" \ Directed mode accepts explicit input wells and output wells. Optional fields include `index_barcode`, `index_euid`, and arbitrary per-well `data`. -### 3. Sequencing-Library Pool Tube +### 4. Sequencing-Library Pool Tube Pools accept input tube EUIDs, well EUIDs, content EUIDs, or existing pool EUIDs. The action creates or fills a pool tube and creates sequencing-library pool content. @@ -123,7 +150,58 @@ curl -sS -X POST "$BLOOM_URL/api/v1/lab-actions/seq-library-pools" \ Supported platform metadata values are `ILMN`, `ONT`, `Ultima`, `PacBio`, and `CompleteGenomics`. -### 4. Sequencing Run Set +### 5. Generic Lab Sets + +Sets use the `set/run-set/generic/1.01` template for lightweight batch/run grouping. They can hold Bloom EUID members, external EUID/string members, operator/instrument/reagent metadata, status, and arbitrary metadata. Set membership is modeled with lineage and stored properties; it does not drive queues or execution. + +```bash +curl -sS -X POST "$BLOOM_URL/api/v1/lab-actions/sets" \ + -H "Authorization: Bearer $BLOOM_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "extraction reagent set", + "description": "Reagents, machine, and operator for extraction batch", + "members": ["Z-BEQ-INSTRUMENT"], + "external_members": ["lot:REAGENT-2026-06-18"], + "operator": "operator@example.com", + "status": "created", + "metadata": {"bench": "SSF"} + }' +``` + +Members can be appended later: + +```bash +curl -sS -X POST "$BLOOM_URL/api/v1/lab-actions/sets/Z-BGS-SET/members" \ + -H "Authorization: Bearer $BLOOM_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"members": ["Z-BCT-TUBE"], "external_members": ["vendor-kit:123"]}' +``` + +### 6. Plate-Well Associated Data + +Per-well data can be attached to a well or the well content. This is intentionally generic so operators can capture QC, quantification, or other measurements without introducing a new workflow engine. + +```bash +curl -sS -X POST "$BLOOM_URL/api/v1/lab-actions/plate-well-data" \ + -H "Authorization: Bearer $BLOOM_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "plate_euid": "Z-BCP-EXTRACTION", + "records": [ + { + "row": "A", + "col": 1, + "data_template_code": "data/quantification/gdna/1.0/", + "name": "A1 gDNA quant", + "target": "content", + "data": {"concentration_ng_ul": 43.2} + } + ] + }' +``` + +### 7. Sequencing Run Set Sequencing run setup creates a `set/run-set/generic/1.01` object with pool, operator, instrument, reagent, flowcell, status, and platform metadata. @@ -160,6 +238,32 @@ The sample sheet includes the run-set EUID and library mappings so OWY can walk Non-ILMN sample-sheet downloads fail explicitly until concrete file formats are specified. There is no fallback manifest. +### Spreadsheet Uploads + +Spreadsheet uploads are available at: + +```bash +curl -sS -X POST "$BLOOM_URL/api/v1/lab-actions/spreadsheet-import?dry_run=true" \ + -H "Authorization: Bearer $BLOOM_TOKEN" \ + -F "file=@lab-actions.xlsx" +``` + +Use `dry_run=false` to execute supported sheets after previewing the parsed operations. The parser accepts `.xlsx` and `.csv`; other extensions fail explicitly. The parser normalizes common operator header typos such as `Continer`, `Conteiner`, and `Templae`. + +Executable sheet names: + +| Sheet | Purpose | Key columns | +|---|---|---| +| `Extraction Plate` | Create or fill extraction plate from incoming tubes. | `tube_euid`, `row`, `col`, `plate_euid`, `plate_name`, `run_set_name`, `print_labels` | +| `Extraction QC` | Fill extraction QC plate from extraction wells. | `source_plate_euid`, `source_well_euid`, `source_row`, `source_col`, `qc_plate_euid`, `qc_plate_name`, `qc_row`, `qc_col`, `result`, `status`, `data` | +| `Seq Library Plate` | Create library plate from extraction wells. | `source_plate_euid`, `source_well_euid`, `source_row`, `source_col`, `output_row`, `output_col`, `index_barcode`, `index_euid`, `data` | +| `Seq Pool` | Create/fill pool tube from wells, tubes, contents, or pools. | `input_euids`, `pool_tube_euid`, `pool_name`, `pool_content_name`, `platform`, `run_set_name`, `print_labels` | +| `Seq Run Set` | Create sequencing run set from a pool and run metadata. | `pool_tube_euid`, `pool_content_euid`, `platform`, `operator`, `instrument_euid`, `flowcell_barcode`, `reagent_euids`, `status`, `metadata` | +| `Sets` | Create generic sets. | `name`, `description`, `members`, `external_members`, `operator`, `instrument`, `reagents`, `machine`, `flowcell_barcode`, `status`, `metadata` | +| `Plate Well Data` | Attach data records to wells or contents. | `plate_euid`, `well_euid`, `row`, `col`, `target`, `data_template_code`, `name`, `data` | + +The example workbook family with `Bulk Create`, `Transfer`, and `Annotation` sheets is parsed and previewed as container-interaction evidence. Those sheets are not blindly executed because they can describe broad generic operations; executable lab-action sheets use the specific names above. + ### Plate Mapping CSV Any plate-like container can export a one-degree mapping CSV: @@ -209,14 +313,19 @@ Recursive templates, such as 96-well plates, reject `count > 1` because a single Open `/lab-actions`. -The wizard has four panels: +The wizard has these panels: 1. Extraction Plate. -2. Sequencing-Library Plate. -3. Sequencing Pool Tube. -4. Sequencing Run Set. +2. Extraction QC Plate. +3. Sequencing-Library Plate. +4. Sequencing Pool Tube. +5. Sequencing Run Set. +6. EUID Barcode Printing. +7. Generic Set Management. +8. Plate-Well Data. +9. Spreadsheet Upload. -It also has a generic EUID print panel and an action-results panel. Successful actions populate downstream fields where possible, for example extraction plate EUID into the library plate step and pool tube/content EUIDs into the run-set step. +Successful actions populate downstream fields where possible, for example extraction plate EUID into the QC and library plate steps and pool tube/content EUIDs into the run-set step. ## Production Rollout Checklist @@ -230,7 +339,7 @@ Use this checklist when updating production later. source ./activate ruff check bloom_lims/api/v1/__init__.py bloom_lims/api/v1/lab_actions.py bloom_lims/api/v1/object_creation.py bloom_lims/api/v1/objects.py bloom_lims/domain/lab_actions.py bloom_lims/schemas/lab_actions.py bloom_lims/schemas/objects.py bloom_lims/gui/routes/modern.py tests/test_lab_actions_api.py tests/test_bloom_prefix_taxonomy.py python -m pytest tests/test_lab_actions_api.py tests/test_bloom_prefix_taxonomy.py::test_wet_lab_templates_use_bloom_prefix_taxonomy tests/test_route_coverage_gaps_api.py::test_object_creation_plate_creates_96_linked_wells tests/test_template_instantiation_recursion.py -q --no-cov - python -m pytest tests/test_lab_actions_api.py tests/test_bloom_prefix_taxonomy.py::test_wet_lab_templates_use_bloom_prefix_taxonomy tests/test_route_coverage_gaps_api.py::test_object_creation_plate_creates_96_linked_wells tests/test_template_instantiation_recursion.py tests/test_api_v1.py::TestObjectCreationAPI tests/test_api_v1.py::TestObjectCreationPathTraversal -q --cov-reset --cov=bloom_lims.api.v1.lab_actions --cov=bloom_lims.domain.lab_actions --cov=bloom_lims.schemas.lab_actions --cov=bloom_lims.api.v1.object_creation --cov=bloom_lims.schemas.objects --cov-report=term-missing --cov-report=json:coverage-lab-actions.json --cov-fail-under=81 + python -m pytest tests/test_lab_actions_api.py tests/test_bloom_prefix_taxonomy.py::test_wet_lab_templates_use_bloom_prefix_taxonomy tests/test_route_coverage_gaps_api.py::test_object_creation_plate_creates_96_linked_wells tests/test_template_instantiation_recursion.py tests/test_api_v1.py::TestObjectCreationAPI tests/test_api_v1.py::TestObjectCreationPathTraversal -q --cov-reset --cov=bloom_lims.api.v1.lab_actions --cov=bloom_lims.domain.lab_actions --cov=bloom_lims.domain.lab_action_spreadsheets --cov=bloom_lims.schemas.lab_actions --cov=bloom_lims.api.v1.object_creation --cov=bloom_lims.schemas.objects --cov-report=term-missing --cov-report=json:coverage-lab-actions.json --cov-fail-under=81 ``` 4. Release Bloom with an annotated semver tag. diff --git a/docs/plans/20260618T190000Z_bloom_lab_action_spreadsheet_upload_ledger.md b/docs/plans/20260618T190000Z_bloom_lab_action_spreadsheet_upload_ledger.md new file mode 100644 index 0000000..a3dd194 --- /dev/null +++ b/docs/plans/20260618T190000Z_bloom_lab_action_spreadsheet_upload_ledger.md @@ -0,0 +1,75 @@ +# Bloom Lab Actions Spreadsheet Upload Ledger + +Date: 2026-06-18 + +## Summary + +Extend the existing Bloom lab-action layer so extraction QC plate fill, sequencing-library plate generation, plate-well associated data, pool creation from wells/tubes/contents/pools, generic set management, and sequencing run-set creation can be driven through API, GUI, and spreadsheet uploads. + +The attached workbook `Container to Container Interactions (1).xlsx` is a schema example. It has `Bulk Create`, `Transfer`, and `Annotation` sheets with source/target container, child container, content-template, and annotation/data-template columns. This pass supports that family of spreadsheet layout as a parsed/previewed container-interaction workbook and adds executable lab-action sheet shapes for the Bloom wet-lab flow. + +## Boundaries + +- Work stays in Bloom on `jem-dev`. +- No production `day`, no `us-west-2`, no live deploy, no destructive DB repair. +- Use existing TapDB generic instances and lineage records. +- Do not revive queue or betalab workflow-driving behavior. Existing queue/betalab code may remain until replaced. +- No fallback config discovery. Runtime config remains explicit. + +## Gate 0 Inventory + +| Surface | State | +|---|---| +| Branch | `jem-dev` | +| Pre-existing dirty files | Untracked `coverage-lab-actions.json`, untracked `output/` | +| Existing API | `/api/v1/lab-actions/extraction-plates`, `/seq-library-plates`, `/seq-library-pools`, `/seq-runs`, `/plates/{plate_euid}/mapping.csv`, `/print-euids` | +| Existing GUI | `/lab-actions` wizard | +| Existing templates | `set/run-set/generic/1.01`, recursive 96-well plate templates, gDNA, sequencing-library content, sequencing-library pool, sequencing-index, gDNA quant | +| Spreadsheet example | Sheets: `Bulk Create`, `Transfer`, `Annotation`; headers on row 4 | + +## Workstreams + +| ID | Requirement | Status | Evidence | +|---|---|---|---| +| LEDGER-001 | Create ledger and record attached spreadsheet/current source baseline. | COMPLETE | This ledger records the workbook sheets, Bloom branch, pre-existing dirt, current lab-action API, GUI, and template baseline. | +| XLSX-001 | Add dependency-free CSV/XLSX workbook parser for lab-action uploads. | COMPLETE | `bloom_lims/domain/lab_action_spreadsheets.py`; parser accepts `.csv` and `.xlsx`, normalizes operator header typos, and previews the attached workbook family. | +| QC-001 | Add extraction QC plate fill API/domain support. | COMPLETE | `POST /api/v1/lab-actions/extraction-qc-plates`; `LabActionsService.fill_extraction_qc_plate`; tests cover explicit QC well fill and 96-assignment capacity guard. | +| DATA-001 | Add associated data-to-plate-well API/domain support. | COMPLETE | `POST /api/v1/lab-actions/plate-well-data`; supports well or well-content targets with structured data payloads. | +| SET-001 | Add generic set create/read/member-management API/domain support. | COMPLETE | `POST /sets`, `GET /sets/{set_euid}`, and `POST /sets/{set_euid}/members`; uses existing `set/run-set/generic/1.01` template and lineage. | +| IMPORT-001 | Add spreadsheet upload endpoint that can dry-run/execute explicit lab-action sheets and preview container-interaction sheets. | COMPLETE | `POST /api/v1/lab-actions/spreadsheet-import`; tests execute extraction QC, seq-library plate, seq-pool, seq-run set, set, and plate-well-data CSV imports, and preview XLSX `Transfer`. | +| GUI-001 | Expose QC, data, set, and spreadsheet-upload controls on `/lab-actions`. | COMPLETE | `templates/modern/lab_actions.html`; GUI smoke test confirms Extraction QC, Spreadsheet Upload, Manage Sets, and upload action controls render. | +| DOC-001 | Update docs with sheet schemas, API routes, and production rollout guidance. | COMPLETE | `README.md`, `docs/apis.md`, `docs/gui.md`, and `docs/lab_actions.md` updated with endpoints, sheet schemas, GUI panels, and rollout tests. | +| TEST-001 | Add focused parser/API/GUI tests and run validation. | COMPLETE | `ruff check ...` passed; `source ./activate dev && python -m pytest tests/test_lab_actions_api.py -q --no-cov` -> 13 passed; changed-surface coverage command -> 34 passed, total coverage 83.16% >= 81%. | +| REPORT-001 | Final report with test output, changed files, and commit status. | COMPLETE | This ledger plus final chat report. Generated coverage/output artifacts are not committed unless promoted separately. | + +## Spreadsheet Sheet Shapes + +Executable lab-action uploads use these sheet names: + +- `Extraction Plate` +- `Extraction QC` +- `Seq Library Plate` +- `Seq Pool` +- `Seq Run Set` +- `Sets` +- `Plate Well Data` + +Container-interaction workbook sheets from the attached example are parsed for preview/evidence: + +- `Bulk Create` +- `Transfer` +- `Annotation` + +## Acceptance + +- API tests prove QC plate fill, plate-well data, set management, pool from plate well/tube, and spreadsheet import. +- GUI smoke test proves the upload/QC/set controls render on `/lab-actions`. +- Docs explain the spreadsheet columns and current production rollout boundary. +- No queue-driving logic is added. + +## Validation + +- `python -m py_compile bloom_lims/domain/lab_action_spreadsheets.py bloom_lims/domain/lab_actions.py bloom_lims/api/v1/lab_actions.py bloom_lims/schemas/lab_actions.py` -> passed. +- `ruff check bloom_lims/domain/lab_action_spreadsheets.py bloom_lims/domain/lab_actions.py bloom_lims/api/v1/lab_actions.py bloom_lims/schemas/lab_actions.py tests/test_lab_actions_api.py` -> passed. +- `source ./activate dev && python -m pytest tests/test_lab_actions_api.py -q --no-cov` -> 13 passed. +- `source ./activate dev && python -m pytest tests/test_lab_actions_api.py tests/test_bloom_prefix_taxonomy.py::test_wet_lab_templates_use_bloom_prefix_taxonomy tests/test_route_coverage_gaps_api.py::test_object_creation_plate_creates_96_linked_wells tests/test_template_instantiation_recursion.py tests/test_api_v1.py::TestObjectCreationAPI tests/test_api_v1.py::TestObjectCreationPathTraversal -q --cov-reset --cov=bloom_lims.api.v1.lab_actions --cov=bloom_lims.domain.lab_actions --cov=bloom_lims.domain.lab_action_spreadsheets --cov=bloom_lims.schemas.lab_actions --cov=bloom_lims.api.v1.object_creation --cov=bloom_lims.schemas.objects --cov-report=term-missing --cov-report=json:coverage-lab-actions.json --cov-fail-under=81` -> 34 passed, total coverage 83.16%. diff --git a/templates/modern/lab_actions.html b/templates/modern/lab_actions.html index c708dcb..6d6e731 100644 --- a/templates/modern/lab_actions.html +++ b/templates/modern/lab_actions.html @@ -78,7 +78,24 @@

1. Extraction Plate

-

2. Sequencing-Library Plate

+

2. Extraction QC Plate

+
+
+ + + + + + + +
+
+ +
+
+

3. Sequencing-Library Plate

@@ -93,7 +110,7 @@

2. Sequencing-Library Plate

-

3. Sequencing Pool Tube

+

4. Sequencing Pool Tube

@@ -118,7 +135,7 @@

3. Sequencing Pool Tube

-

4. Sequencing Run Set

+

5. Sequencing Run Set

@@ -136,6 +153,62 @@

4. Sequencing Run Set

+
+
+
+

Manage Sets

+
+
+ + + + + + + +
+
+ +
+
+

Plate-Well Data

+
+
+ + + + + + + +
+
+
+ +
+
+

Spreadsheet Upload

+
+
+

+ Upload `.xlsx` or `.csv` sheets for Extraction Plate, Extraction QC, Seq Library Plate, Seq Pool, + Seq Run Set, Sets, or Plate Well Data. Bulk Create, Transfer, and Annotation sheets are parsed as previews. +

+ + + +
+
+

Print EUID Barcodes

@@ -203,12 +276,18 @@

Action Results

if (payload.pool_tube_euid) { euids.push(payload.pool_tube_euid); } + if (payload.qc_plate_euid) { + euids.push(payload.qc_plate_euid); + } if (euids.length) { document.getElementById('print-euids').value = Array.from(new Set(euids)).join('\n'); } if (payload.plate_euid && !document.getElementById('library-source-plate').value) { document.getElementById('library-source-plate').value = payload.plate_euid; } + if (payload.plate_euid && !document.getElementById('qc-source-plate').value) { + document.getElementById('qc-source-plate').value = payload.plate_euid; + } if (payload.mappings) { const libraryInputs = payload.mappings .map((item) => item.library_content_euid || item.output_content_euid) @@ -246,6 +325,50 @@

Action Results

return data; } + function assignmentRows(textareaId) { + return document.getElementById(textareaId).value + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean) + .map((line) => { + const [position, status, ...pairs] = line.split(',').map((value) => value.trim()); + const match = position.match(/^([A-Ha-h])(\d{1,2})$/); + const data = {}; + pairs.forEach((pair) => { + const [key, value] = pair.split('='); + if (key && value !== undefined) { + data[key.trim()] = value.trim(); + } + }); + return { + row: match ? match[1].toUpperCase() : null, + col: match ? Number(match[2]) : null, + status: status || null, + result: status || null, + data, + }; + }); + } + + async function uploadSpreadsheet() { + const input = document.getElementById('spreadsheet-file'); + if (!input.files.length) { + throw new Error('Choose a .xlsx or .csv file first'); + } + const execute = document.getElementById('spreadsheet-execute').checked; + const form = new FormData(); + form.append('file', input.files[0]); + const response = await fetch(`/api/v1/lab-actions/spreadsheet-import?dry_run=${execute ? 'false' : 'true'}`, { + method: 'POST', + body: form, + }); + const data = await response.json().catch(() => ({detail: response.statusText})); + if (!response.ok) { + throw new Error(data.detail || response.statusText); + } + return data; + } + const handlers = { 'create-extraction': async () => postJson('/api/v1/lab-actions/extraction-plates', { mode: 'auto', @@ -258,6 +381,11 @@

Action Results

source_plate_euid: document.getElementById('library-source-plate').value.trim(), plate_name: document.getElementById('library-name').value || 'seq library plate', }), + 'create-qc': async () => postJson('/api/v1/lab-actions/extraction-qc-plates', { + source_plate_euid: document.getElementById('qc-source-plate').value.trim(), + qc_plate_name: document.getElementById('qc-name').value || 'extraction QC plate', + assignments: assignmentRows('qc-assignments'), + }), 'create-pool': async () => postJson('/api/v1/lab-actions/seq-library-pools', { input_euids: lines('pool-inputs'), platform: document.getElementById('pool-platform').value, @@ -271,6 +399,23 @@

Action Results

operator: document.getElementById('run-operator').value.trim(), status: 'created', }), + 'create-set': async () => postJson('/api/v1/lab-actions/sets', { + name: document.getElementById('set-name').value.trim(), + members: lines('set-members'), + external_members: lines('set-external-members'), + status: 'created', + }), + 'attach-data': async () => postJson('/api/v1/lab-actions/plate-well-data', { + data_template_code: document.getElementById('data-template').value.trim(), + records: assignmentRows('data-records').map((item) => ({ + plate_euid: document.getElementById('data-plate').value.trim(), + row: item.row, + col: item.col, + target: 'content', + data: item.data, + })), + }), + 'upload-spreadsheet': uploadSpreadsheet, 'print-euids': async () => postJson('/api/v1/lab-actions/print-euids', { euids: lines('print-euids'), lab: document.getElementById('print-lab').value.trim(), diff --git a/tests/test_lab_actions_api.py b/tests/test_lab_actions_api.py index b5f3953..0e3f23f 100644 --- a/tests/test_lab_actions_api.py +++ b/tests/test_lab_actions_api.py @@ -3,6 +3,8 @@ from __future__ import annotations import os +import zipfile +from io import BytesIO from unittest.mock import patch os.environ["BLOOM_DEV_AUTH_BYPASS"] = "true" @@ -10,6 +12,7 @@ from fastapi.testclient import TestClient +from bloom_lims.domain.lab_action_spreadsheets import parse_workbook from main import app @@ -564,3 +567,303 @@ def test_lab_action_schema_validation_edges() -> None: }, ) assert too_many_prints.status_code == 422 + + +def test_qc_plate_data_sets_and_pool_from_well_and_tube() -> None: + client = _client() + tube_1, _ = _create_filled_tube(client, suffix="qc-a") + tube_2, _ = _create_filled_tube(client, suffix="qc-b") + + with patch( + "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None + ): + extraction = client.post( + "/api/v1/lab-actions/extraction-plates", + json={ + "mode": "auto", + "tube_euids": [tube_1], + "plate_name": "pytest spreadsheet extraction plate", + }, + ) + assert extraction.status_code == 200, extraction.text + extraction_payload = extraction.json() + source_well = extraction_payload["mappings"][0]["well_euid"] + + qc = client.post( + "/api/v1/lab-actions/extraction-qc-plates", + json={ + "source_plate_euid": extraction_payload["plate_euid"], + "qc_plate_name": "pytest extraction QC plate", + "assignments": [ + { + "source_well_euid": source_well, + "qc_row": "A", + "qc_col": 1, + "result": "pass", + "status": "recorded", + "data": {"concentration_ng_ul": 12.3}, + } + ], + }, + ) + assert qc.status_code == 200, qc.text + qc_payload = qc.json() + assert qc_payload["qc_plate_euid"] + assert qc_payload["mappings"][0]["result"] == "pass" + assert qc_payload["mappings"][0]["data_euid"] + + data = client.post( + "/api/v1/lab-actions/plate-well-data", + json={ + "data_template_code": "data/operation/extraction-qc/1.0/", + "records": [ + { + "plate_euid": extraction_payload["plate_euid"], + "row": "A", + "col": 1, + "target": "content", + "data": {"a260_280": 1.82}, + } + ], + }, + ) + assert data.status_code == 200, data.text + assert data.json()["mappings"][0]["data_euid"] + + lab_set = client.post( + "/api/v1/lab-actions/sets", + json={ + "name": "pytest extraction set", + "members": [extraction_payload["plate_euid"], qc_payload["qc_plate_euid"]], + "external_members": ["external-reagent-lot-1"], + }, + ) + assert lab_set.status_code == 200, lab_set.text + set_euid = lab_set.json()["set_euid"] + add_members = client.post( + f"/api/v1/lab-actions/sets/{set_euid}/members", + json={"members": [tube_2], "external_members": ["operator-note-1"]}, + ) + assert add_members.status_code == 200, add_members.text + fetched = client.get(f"/api/v1/lab-actions/sets/{set_euid}") + assert fetched.status_code == 200 + assert tube_2 in fetched.json()["members"] + assert "operator-note-1" in fetched.json()["external_members"] + + with patch( + "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None + ): + pool = client.post( + "/api/v1/lab-actions/seq-library-pools", + json={ + "input_euids": [source_well, tube_2], + "platform": "ILMN", + "pool_name": "pytest well plus tube pool", + }, + ) + assert pool.status_code == 200, pool.text + assert len(pool.json()["member_euids"]) == 2 + + +def _minimal_xlsx(sheet_name: str, rows: list[list[str | None]]) -> bytes: + def cell_ref(row_index: int, col_index: int) -> str: + col = "" + value = col_index + while value: + value, remainder = divmod(value - 1, 26) + col = chr(65 + remainder) + col + return f"{col}{row_index}" + + sheet_rows = [] + for row_index, row in enumerate(rows, start=1): + cells = [] + for col_index, value in enumerate(row, start=1): + if value is None: + continue + cells.append( + f'{value}' + ) + sheet_rows.append(f'{"".join(cells)}') + worksheet = ( + '' + '' + f'{"".join(sheet_rows)}' + ) + output = BytesIO() + with zipfile.ZipFile(output, "w") as archive: + archive.writestr( + "[Content_Types].xml", + '' + '' + '' + '' + "", + ) + archive.writestr( + "xl/workbook.xml", + '' + '' + f'', + ) + archive.writestr( + "xl/_rels/workbook.xml.rels", + '' + '' + '' + "", + ) + archive.writestr("xl/worksheets/sheet1.xml", worksheet) + return output.getvalue() + + +def test_spreadsheet_import_csv_and_xlsx_preview() -> None: + client = _client() + tube, _ = _create_filled_tube(client, suffix="spreadsheet") + csv_bytes = ( + "tube_euid,row,col,plate_name\n" + f"{tube},A,1,pytest upload extraction plate\n" + ).encode() + dry_run = client.post( + "/api/v1/lab-actions/spreadsheet-import", + params={"dry_run": "true"}, + files={"file": ("extraction.csv", csv_bytes, "text/csv")}, + ) + assert dry_run.status_code == 200, dry_run.text + assert dry_run.json()["actions"][0]["action"] == "extraction_plate" + assert dry_run.json()["actions"][0]["request"]["mode"] == "directed" + + with patch( + "bloom_lims.integrations.atlas.events.emit_bloom_event", return_value=None + ): + execute = client.post( + "/api/v1/lab-actions/spreadsheet-import", + params={"dry_run": "false"}, + files={"file": ("extraction.csv", csv_bytes, "text/csv")}, + ) + assert execute.status_code == 200, execute.text + assert execute.json()["actions"][0]["result"]["plate_euid"] + + workbook = _minimal_xlsx( + "Transfer", + [ + [], + [], + ["SOURCE CONTAINER(s)", None, None, None, None, None, None, None, None, "DESTINATION CONTAINER(s)"], + ["Container EUID", "Container Template EUID", "Continer Cat/Type/Subtype"], + ["BCT-EXAMPLE", "container/tube/tube-generic-10ml/1.0", "container/tube/generic"], + ], + ) + parsed = parse_workbook("container_interactions.xlsx", workbook) + assert parsed[0].name == "Transfer" + assert "container_euid" in parsed[0].headers + preview = client.post( + "/api/v1/lab-actions/spreadsheet-import", + params={"dry_run": "true"}, + files={ + "file": ( + "container_interactions.xlsx", + workbook, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + }, + ) + assert preview.status_code == 200, preview.text + assert preview.json()["actions"][0]["action"] == "container_interaction_preview" + + +def _upload_csv(client: TestClient, csv_text: str, *, dry_run: bool = False): + return client.post( + "/api/v1/lab-actions/spreadsheet-import", + params={"dry_run": str(dry_run).lower()}, + files={"file": ("lab_action.csv", csv_text.encode(), "text/csv")}, + ) + + +def test_spreadsheet_import_executes_each_lab_action_sheet_shape() -> None: + client = _client() + tube, _ = _create_filled_tube(client, suffix="upload-flow") + extraction = client.post( + "/api/v1/lab-actions/extraction-plates", + json={"mode": "auto", "tube_euids": [tube]}, + ) + assert extraction.status_code == 200, extraction.text + extraction_payload = extraction.json() + source_well = extraction_payload["mappings"][0]["well_euid"] + + qc_csv = ( + "source_well_euid,qc_row,qc_col,result,status,data_concentration_ng_ul\n" + f"{source_well},A,1,pass,recorded,11.2\n" + ) + qc = _upload_csv(client, qc_csv) + assert qc.status_code == 200, qc.text + qc_action = qc.json()["actions"][0] + assert qc_action["action"] == "extraction_qc" + assert qc_action["result"]["mappings"][0]["result"] == "pass" + + library_csv = ( + "source_well_euid,row,col,index_barcode,data_cycles\n" + f"{source_well},A,1,ACGTACGT,8\n" + ) + library = _upload_csv(client, library_csv) + assert library.status_code == 200, library.text + library_action = library.json()["actions"][0] + assert library_action["action"] == "seq_library_plate" + library_content = library_action["result"]["mappings"][0]["library_content_euid"] + + pool_csv = ( + "input_euids,platform,pool_name,pool_batch\n" + f"{library_content},ILMN,pytest upload pool,batch-1\n" + ) + pool = _upload_csv(client, pool_csv) + assert pool.status_code == 200, pool.text + pool_action = pool.json()["actions"][0] + assert pool_action["action"] == "seq_pool" + pool_result = pool_action["result"] + + run_csv = ( + "pool_tube_euid,pool_content_euid,platform,operator,flowcell_barcode,status,run_batch\n" + f"{pool_result['pool_tube_euid']},{pool_result['pool_content_euid']},ILMN,pytest@example.com,FLOWCELL-UPLOAD,created,batch-1\n" + ) + run = _upload_csv(client, run_csv) + assert run.status_code == 200, run.text + run_action = run.json()["actions"][0] + assert run_action["action"] == "seq_run_set" + assert run_action["result"]["set_euid"] + + set_csv = ( + "name,members,external_members,status,set_note\n" + f"pytest upload set,{extraction_payload['plate_euid']};{pool_result['pool_tube_euid']},lot:123,created,upload\n" + ) + lab_set = _upload_csv(client, set_csv) + assert lab_set.status_code == 200, lab_set.text + set_action = lab_set.json()["actions"][0] + assert set_action["action"] == "sets" + assert set_action["result"][0]["set_euid"] + + data_csv = ( + "well_euid,data_template_code,target,name,annotation_value,metric_a260_280\n" + f"{source_well},data/operation/extraction-qc/1.0/,content,pytest uploaded data,ok,1.82\n" + ) + data = _upload_csv(client, data_csv) + assert data.status_code == 200, data.text + data_action = data.json()["actions"][0] + assert data_action["action"] == "plate_well_data" + assert data_action["result"]["mappings"][0]["data_euid"] + + bad_extension = client.post( + "/api/v1/lab-actions/spreadsheet-import", + files={"file": ("lab_action.txt", b"not,csv", "text/plain")}, + ) + assert bad_extension.status_code == 400 + assert "must be .csv or .xlsx" in bad_extension.json()["detail"] + + +def test_lab_actions_gui_renders_upload_qc_and_set_controls() -> None: + client = _client() + response = client.get("/lab-actions") + assert response.status_code == 200, response.text + assert "Extraction QC Plate" in response.text + assert "Spreadsheet Upload" in response.text + assert "Manage Sets" in response.text + assert 'data-action="upload-spreadsheet"' in response.text From 0d57f0aef8ac6d91b18fc67fd44742f40d3073f2 Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 18 Jun 2026 10:55:37 -0700 Subject: [PATCH 39/47] Release 7.0.17 --- bloom_lims/api/v1/lab_actions.py | 6 +- bloom_lims/domain/lab_actions.py | 63 ++++++++++++- templates/modern/lab_actions.html | 150 +++++++++++++++++++----------- tests/test_lab_actions_api.py | 14 ++- 4 files changed, 170 insertions(+), 63 deletions(-) diff --git a/bloom_lims/api/v1/lab_actions.py b/bloom_lims/api/v1/lab_actions.py index 591a32e..99191af 100644 --- a/bloom_lims/api/v1/lab_actions.py +++ b/bloom_lims/api/v1/lab_actions.py @@ -177,12 +177,12 @@ async def download_seq_run_sample_sheet( ): service = _service_for_user(user) try: - content = service.illumina_sample_sheet(set_euid) + content, filename, media_type = service.sequencing_sample_sheet_download(set_euid) return PlainTextResponse( content, - media_type="text/csv", + media_type=media_type, headers={ - "Content-Disposition": f'attachment; filename="{set_euid}_SampleSheet.csv"' + "Content-Disposition": f'attachment; filename="{filename}"' }, ) except Exception as exc: diff --git a/bloom_lims/domain/lab_actions.py b/bloom_lims/domain/lab_actions.py index 6579bc1..9c55789 100644 --- a/bloom_lims/domain/lab_actions.py +++ b/bloom_lims/domain/lab_actions.py @@ -866,14 +866,10 @@ def plate_mapping_csv(self, plate_euid: str) -> str: writer.writerows(rows) return output.getvalue() - def illumina_sample_sheet(self, set_euid: str) -> str: + def sequencing_sample_sheet(self, set_euid: str) -> str: run_set = self._require(set_euid) props = self._props(run_set) platform = str(props.get("platform") or "").strip() - if platform != "ILMN": - raise ValueError( - f"Sample sheet download is only implemented for ILMN, not {platform or 'unknown'}" - ) pool_content = self._require(str(props.get("pool_content_euid") or "")) library_inputs = [ lineage.parent_instance @@ -916,6 +912,26 @@ def illumina_sample_sheet(self, set_euid: str) -> str: ), } ) + if platform == "ONT": + return self._ont_sample_sheet(run_set=run_set, props=props, data_rows=data_rows) + if platform != "ILMN": + raise ValueError( + f"Sample sheet download is implemented for ILMN and ONT, not {platform or 'unknown'}" + ) + return self._illumina_sample_sheet(run_set=run_set, props=props, data_rows=data_rows) + + def sequencing_sample_sheet_download(self, set_euid: str) -> tuple[str, str, str]: + run_set = self._require(set_euid) + props = self._props(run_set) + platform = str(props.get("platform") or "").strip() + content = self.sequencing_sample_sheet(set_euid) + if platform == "ONT": + return content, f"{set_euid}_ONT_manifest.tsv", "text/tab-separated-values" + return content, f"{set_euid}_{platform or 'sequencing'}_SampleSheet.csv", "text/csv" + + def _illumina_sample_sheet( + self, *, run_set, props: dict[str, Any], data_rows: list[dict[str, str]] + ) -> str: output = io.StringIO() output.write("[Header]\n") output.write(f"RunSetEUID,{run_set.euid}\n") @@ -929,6 +945,43 @@ def illumina_sample_sheet(self, set_euid: str) -> str: writer.writerows(data_rows) return output.getvalue() + def _ont_sample_sheet( + self, *, run_set, props: dict[str, Any], data_rows: list[dict[str, str]] + ) -> str: + output = io.StringIO() + fieldnames = [ + "sample_id", + "alias", + "barcode", + "run_set_euid", + "library_euid", + "source_gdna_euid", + "source_specimen_content_euid", + "flowcell_barcode", + "operator", + ] + writer = csv.DictWriter(output, fieldnames=fieldnames, delimiter="\t") + writer.writeheader() + for row in data_rows: + description = json.loads(row.get("Description") or "{}") + writer.writerow( + { + "sample_id": row.get("Sample_ID") or "", + "alias": row.get("Sample_Name") or row.get("Sample_ID") or "", + "barcode": row.get("index") or "", + "run_set_euid": run_set.euid, + "library_euid": description.get("library_euid") or row.get("Sample_ID") or "", + "source_gdna_euid": description.get("source_gdna_euid") or "", + "source_specimen_content_euid": description.get( + "source_specimen_content_euid" + ) + or "", + "flowcell_barcode": props.get("flowcell_barcode") or "", + "operator": props.get("operator") or "", + } + ) + return output.getvalue() + def _split_values(self, value: Any) -> list[str]: if value in (None, ""): return [] diff --git a/templates/modern/lab_actions.html b/templates/modern/lab_actions.html index 6d6e731..71f4c2c 100644 --- a/templates/modern/lab_actions.html +++ b/templates/modern/lab_actions.html @@ -61,16 +61,16 @@

1. Extraction Plate

- +
- +
-
@@ -82,12 +82,12 @@

2. Extraction QC Plate

- + - + - -
@@ -99,10 +99,12 @@

3. Sequencing-Library Plate

- + - -
@@ -114,10 +116,10 @@

4. Sequencing Pool Tube

- +
- @@ -126,8 +128,8 @@

4. Sequencing Pool Tube

- -
@@ -139,14 +141,22 @@

5. Sequencing Run Set

- + - + + + - + - -
@@ -160,12 +170,12 @@

Manage Sets

- + - + - -
@@ -177,12 +187,12 @@

Plate-Well Data

- + - + - -
@@ -198,12 +208,12 @@

Spreadsheet Upload

Upload `.xlsx` or `.csv` sheets for Extraction Plate, Extraction QC, Seq Library Plate, Seq Pool, Seq Run Set, Sets, or Plate Well Data. Bulk Create, Transfer, and Annotation sheets are parsed as previews.

- + - @@ -215,22 +225,22 @@

Print EUID Barcodes

- +
- +
- +
- +
-
@@ -242,7 +252,7 @@

Action Results

-
{}
+
{}
{% endblock %} @@ -265,7 +275,8 @@

Action Results

links.appendChild(link(`/api/v1/lab-actions/plates/${payload.plate_euid}/mapping.csv`, 'Download plate CSV')); } if (payload.set_euid) { - links.appendChild(link(`/api/v1/lab-actions/seq-runs/${payload.set_euid}/samplesheet`, 'Download ILMN sample sheet')); + const platform = payload.platform || document.getElementById('run-platform').value || 'sequencing'; + links.appendChild(link(`/api/v1/lab-actions/seq-runs/${payload.set_euid}/samplesheet`, `Download ${platform} sample sheet`)); } const euids = []; ['new_container_euids', 'created_euids'].forEach((key) => { @@ -325,6 +336,17 @@

Action Results

return data; } + function keyValueData(pairs) { + const data = {}; + pairs.forEach((pair) => { + const [key, value] = pair.split('='); + if (key && value !== undefined) { + data[key.trim()] = value.trim(); + } + }); + return data; + } + function assignmentRows(textareaId) { return document.getElementById(textareaId).value .split(/\r?\n/) @@ -333,19 +355,33 @@

Action Results

.map((line) => { const [position, status, ...pairs] = line.split(',').map((value) => value.trim()); const match = position.match(/^([A-Ha-h])(\d{1,2})$/); - const data = {}; - pairs.forEach((pair) => { - const [key, value] = pair.split('='); - if (key && value !== undefined) { - data[key.trim()] = value.trim(); - } - }); return { row: match ? match[1].toUpperCase() : null, col: match ? Number(match[2]) : null, status: status || null, result: status || null, - data, + data: keyValueData(pairs), + }; + }); + } + + function libraryAssignmentRows(textareaId) { + return document.getElementById(textareaId).value + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean) + .map((line) => { + const [sourceWellEuid, position, indexBarcode, indexEuid, ...pairs] = line + .split(',') + .map((value) => value.trim()); + const match = (position || '').match(/^([A-Ha-h])(\d{1,2})$/); + return { + source_well_euid: sourceWellEuid, + row: match ? match[1].toUpperCase() : null, + col: match ? Number(match[2]) : null, + index_barcode: indexBarcode || null, + index_euid: indexEuid || null, + data: keyValueData(pairs), }; }); } @@ -376,11 +412,21 @@

Action Results

plate_name: document.getElementById('extract-name').value || 'extraction plate', create_run_set: document.getElementById('extract-run-set').checked, }), - 'create-library': async () => postJson('/api/v1/lab-actions/seq-library-plates', { - mode: 'plate_1_to_1', - source_plate_euid: document.getElementById('library-source-plate').value.trim(), - plate_name: document.getElementById('library-name').value || 'seq library plate', - }), + 'create-library': async () => { + const assignments = libraryAssignmentRows('library-assignments'); + if (assignments.length) { + return postJson('/api/v1/lab-actions/seq-library-plates', { + mode: 'directed', + assignments, + plate_name: document.getElementById('library-name').value || 'seq library plate', + }); + } + return postJson('/api/v1/lab-actions/seq-library-plates', { + mode: 'plate_1_to_1', + source_plate_euid: document.getElementById('library-source-plate').value.trim(), + plate_name: document.getElementById('library-name').value || 'seq library plate', + }); + }, 'create-qc': async () => postJson('/api/v1/lab-actions/extraction-qc-plates', { source_plate_euid: document.getElementById('qc-source-plate').value.trim(), qc_plate_name: document.getElementById('qc-name').value || 'extraction QC plate', @@ -394,7 +440,7 @@

Action Results

'create-run': async () => postJson('/api/v1/lab-actions/seq-runs', { pool_tube_euid: document.getElementById('run-pool-tube').value.trim(), pool_content_euid: document.getElementById('run-pool-content').value.trim() || null, - platform: 'ILMN', + platform: document.getElementById('run-platform').value, flowcell_barcode: document.getElementById('run-flowcell').value.trim(), operator: document.getElementById('run-operator').value.trim(), status: 'created', diff --git a/tests/test_lab_actions_api.py b/tests/test_lab_actions_api.py index 0e3f23f..8a1b081 100644 --- a/tests/test_lab_actions_api.py +++ b/tests/test_lab_actions_api.py @@ -213,7 +213,7 @@ def test_lab_actions_full_ilmn_flow_and_csv_export() -> None: assert library_content in sample_sheet.text -def test_lab_actions_validation_and_non_ilmn_samplesheet_rejection() -> None: +def test_lab_actions_validation_and_ont_samplesheet_download() -> None: client = _client() invalid = client.post( "/api/v1/lab-actions/extraction-plates", @@ -247,8 +247,12 @@ def test_lab_actions_validation_and_non_ilmn_samplesheet_rejection() -> None: sample_sheet = client.get( f"/api/v1/lab-actions/seq-runs/{run.json()['set_euid']}/samplesheet" ) - assert sample_sheet.status_code == 400 - assert "only implemented for ILMN" in sample_sheet.json()["detail"] + assert sample_sheet.status_code == 200, sample_sheet.text + assert sample_sheet.headers["content-type"].startswith("text/tab-separated-values") + assert "_ONT_manifest.tsv" in sample_sheet.headers["content-disposition"] + assert "sample_id\talias\tbarcode\trun_set_euid" in sample_sheet.text + assert run.json()["set_euid"] in sample_sheet.text + assert "ONT-FLOWCELL" in sample_sheet.text def test_object_creation_count_and_print_euid_mock() -> None: @@ -310,6 +314,10 @@ def test_lab_actions_gui_renders_wizard() -> None: assert "Sequencing Pool Tube" in html assert "Sequencing Run Set" in html assert "/api/v1/lab-actions/extraction-plates" in html + assert 'data-testid="bloom-lab-library-assignments"' in html + assert 'data-testid="bloom-lab-run-platform"' in html + assert "mode: 'directed'" in html + assert "Download ${platform} sample sheet" in html def test_directed_extraction_into_existing_plate_with_quant_and_reuse_guard() -> None: From 5861cb33d2b2159d66c4e000bb72b10fe2aa46f5 Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 18 Jun 2026 15:54:26 -0700 Subject: [PATCH 40/47] Complete Bloom lab-action live proof support --- bloom_lims/domain/lab_actions.py | 81 ++++++++++++++++++++++++++++++- bloom_lims/schemas/lab_actions.py | 1 + templates/modern/lab_actions.html | 5 ++ tests/test_lab_actions_api.py | 10 +++- 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/bloom_lims/domain/lab_actions.py b/bloom_lims/domain/lab_actions.py index 9c55789..1b1fc6c 100644 --- a/bloom_lims/domain/lab_actions.py +++ b/bloom_lims/domain/lab_actions.py @@ -46,6 +46,9 @@ POOL_CONTENT_TEMPLATE_CODE = "content/pool/sequencing-library/1.0/" GDNA_QUANT_TEMPLATE_CODE = "data/quantification/gdna/1.0/" EXTRACTION_QC_TEMPLATE_CODE = "data/operation/extraction-qc/1.0/" +SEQUENCED_LIBRARY_ASSIGNMENT_TEMPLATE_CODE = ( + "data/library-index-assignment/sequencing-library/1.0/" +) def re_split_multi(value: str) -> list[str]: @@ -795,6 +798,11 @@ def create_seq_run_set(self, request: SeqRunSetRequest) -> dict[str, Any]: self._create_lineage(run_set.euid, request.instrument_euid, "run_uses_instrument") for reagent_euid in request.reagent_euids: self._create_lineage(run_set.euid, reagent_euid, "run_uses_reagent") + assignments = self._create_sequenced_library_assignments( + run_set=run_set, + pool_content=pool_content, + flowcell_barcode=request.flowcell_barcode, + ) self.bdb.session.commit() return { "set_euid": run_set.euid, @@ -802,8 +810,65 @@ def create_seq_run_set(self, request: SeqRunSetRequest) -> dict[str, Any]: "platform": request.platform, "pool_tube_euid": pool_tube.euid, "pool_content_euid": pool_content.euid, + "assignments": assignments, } + def _create_sequenced_library_assignments( + self, + *, + run_set, + pool_content, + flowcell_barcode: str, + ) -> list[dict[str, str]]: + assignments: list[dict[str, str]] = [] + source_libraries = [ + lineage.parent_instance + for lineage in self._lineages_to_child(pool_content, "DERIVED_FROM") + ] + for index, library in enumerate(source_libraries, start=1): + props = self._props(library) + barcode = str(props.get("index_barcode") or "").strip() + lane = str(props.get("lane") or props.get("lane_index") or "1").strip() + assignment = self._create_by_code( + SEQUENCED_LIBRARY_ASSIGNMENT_TEMPLATE_CODE, + name=f"{run_set.euid}:{lane}:{barcode or library.euid}", + properties={ + "flowcell_id": flowcell_barcode, + "lane": lane, + "library_barcode": barcode, + "library_prep_output_euid": library.euid, + "library_material_euid": library.euid, + "barcode_reagent_euid": str(props.get("index_euid") or "").strip(), + "metadata": { + "created_by": "bloom_lab_actions", + "pool_content_euid": pool_content.euid, + }, + }, + ) + self._create_lineage( + run_set.euid, + assignment.euid, + "beta_sequenced_library_assignment", + ) + self._create_lineage( + library.euid, + assignment.euid, + "beta_assignment_source", + edge_type="RUN_CONSUMED", + source_role="sequencing_run_set", + target_role="library_material", + ) + assignments.append( + { + "assignment_euid": assignment.euid, + "library_content_euid": library.euid, + "flowcell_id": flowcell_barcode, + "lane": lane, + "library_barcode": barcode, + } + ) + return assignments + def plate_mapping_rows(self, plate_euid: str) -> list[dict[str, str]]: plate = self._require(plate_euid) wells = self._plate_wells(plate) @@ -1302,10 +1367,22 @@ def import_spreadsheet( return {"filename": filename, "dry_run": dry_run, "actions": actions} def print_euids(self, request: PrintEuidRequest) -> dict[str, Any]: - service = ZebraDayService() results = [] for euid in request.euids: self._require(euid) + if request.dry_run: + results.append( + { + "dry_run": True, + "lab": request.lab, + "printer_id": request.printer_id, + "label_zpl_style": request.label_zpl_style, + "euid": euid, + "copies": request.copies, + } + ) + continue + service = ZebraDayService() results.append( service.submit_print_job( lab=request.lab, @@ -1315,4 +1392,4 @@ def print_euids(self, request: PrintEuidRequest) -> dict[str, Any]: print_n=request.copies, ) ) - return {"printed": len(results), "results": results} + return {"printed": 0 if request.dry_run else len(results), "dry_run": request.dry_run, "results": results} diff --git a/bloom_lims/schemas/lab_actions.py b/bloom_lims/schemas/lab_actions.py index 73c281c..9757601 100644 --- a/bloom_lims/schemas/lab_actions.py +++ b/bloom_lims/schemas/lab_actions.py @@ -260,6 +260,7 @@ class PrintEuidRequest(BaseModel): printer_id: str label_zpl_style: str copies: int = Field(default=1, ge=1, le=20) + dry_run: bool = False @field_validator("euids") @classmethod diff --git a/templates/modern/lab_actions.html b/templates/modern/lab_actions.html index 71f4c2c..729f562 100644 --- a/templates/modern/lab_actions.html +++ b/templates/modern/lab_actions.html @@ -240,6 +240,10 @@

Print EUID Barcodes

+ @@ -468,6 +472,7 @@

Action Results

printer_id: document.getElementById('print-printer').value.trim(), label_zpl_style: document.getElementById('print-style').value.trim(), copies: 1, + dry_run: document.getElementById('print-dry-run').checked, }), }; diff --git a/tests/test_lab_actions_api.py b/tests/test_lab_actions_api.py index 8a1b081..764a4d0 100644 --- a/tests/test_lab_actions_api.py +++ b/tests/test_lab_actions_api.py @@ -201,6 +201,9 @@ def test_lab_actions_full_ilmn_flow_and_csv_export() -> None: assert seq_run.status_code == 200, seq_run.text run_payload = seq_run.json() assert run_payload["set_euid"] + assert run_payload["assignments"] + assert run_payload["assignments"][0]["flowcell_id"] == "PYTEST-FLOWCELL" + assert run_payload["assignments"][0]["lane"] == "1" sample_sheet = client.get( f"/api/v1/lab-actions/seq-runs/{run_payload['set_euid']}/samplesheet" @@ -282,11 +285,14 @@ def test_object_creation_count_and_print_euid_mock() -> None: "printer_id": "pytest-printer", "label_zpl_style": "euid", "copies": 1, + "dry_run": True, }, ) assert response.status_code == 200, response.text - assert response.json()["printed"] == 3 - assert service.submit_print_job.call_count == 3 + assert response.json()["printed"] == 0 + assert response.json()["dry_run"] is True + assert service.submit_print_job.call_count == 0 + assert len(response.json()["results"]) == 3 recursive_batch = client.post( "/api/v1/object-creation/create", From 56d2961da56859b3b77e521f1e68c107e5c32472 Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 18 Jun 2026 16:51:54 -0700 Subject: [PATCH 41/47] Return plate identifiers from lab-action data attach --- bloom_lims/domain/lab_actions.py | 7 +++++++ tests/test_lab_actions_api.py | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/bloom_lims/domain/lab_actions.py b/bloom_lims/domain/lab_actions.py index 1b1fc6c..99fcbdc 100644 --- a/bloom_lims/domain/lab_actions.py +++ b/bloom_lims/domain/lab_actions.py @@ -476,8 +476,11 @@ def fill_extraction_qc_plate( def attach_plate_well_data(self, request: PlateWellDataRequest) -> dict[str, Any]: mappings: list[dict[str, Any]] = [] + plate_euids: set[str] = set() for record in request.records: well = self._well_for_data_record(record) + if record.plate_euid: + plate_euids.add(record.plate_euid) target = well if record.target == "well" else self._well_content(well) data = self._create_by_code( request.data_template_code, @@ -493,6 +496,7 @@ def attach_plate_well_data(self, request: PlateWellDataRequest) -> dict[str, Any mappings.append( { "well_euid": well.euid, + "plate_euid": record.plate_euid, "target_euid": target.euid, "data_euid": data.euid, "relationship_type": request.relationship_type, @@ -505,7 +509,10 @@ def attach_plate_well_data(self, request: PlateWellDataRequest) -> dict[str, Any member_euids=[item["data_euid"] for item in mappings], ) self.bdb.session.commit() + sorted_plate_euids = sorted(plate_euids) return { + "plate_euid": sorted_plate_euids[0] if len(sorted_plate_euids) == 1 else None, + "plate_euids": sorted_plate_euids, "run_set_euid": run_set.euid if run_set is not None else None, "mappings": mappings, } diff --git a/tests/test_lab_actions_api.py b/tests/test_lab_actions_api.py index 764a4d0..011a05b 100644 --- a/tests/test_lab_actions_api.py +++ b/tests/test_lab_actions_api.py @@ -642,7 +642,11 @@ def test_qc_plate_data_sets_and_pool_from_well_and_tube() -> None: }, ) assert data.status_code == 200, data.text - assert data.json()["mappings"][0]["data_euid"] + data_payload = data.json() + assert data_payload["plate_euid"] == extraction_payload["plate_euid"] + assert data_payload["plate_euids"] == [extraction_payload["plate_euid"]] + assert data_payload["mappings"][0]["plate_euid"] == extraction_payload["plate_euid"] + assert data_payload["mappings"][0]["data_euid"] lab_set = client.post( "/api/v1/lab-actions/sets", From 2d330b9726a51cbd64152590031c889aaf78fb5d Mon Sep 17 00:00:00 2001 From: John Major Date: Thu, 18 Jun 2026 17:32:40 -0700 Subject: [PATCH 42/47] Record live lab action E2E proof --- ...18T000000Z_bloom_lab_action_flow_ledger.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/plans/20260618T000000Z_bloom_lab_action_flow_ledger.md b/docs/plans/20260618T000000Z_bloom_lab_action_flow_ledger.md index 8144825..a2befa9 100644 --- a/docs/plans/20260618T000000Z_bloom_lab_action_flow_ledger.md +++ b/docs/plans/20260618T000000Z_bloom_lab_action_flow_ledger.md @@ -81,3 +81,36 @@ Build a Bloom-owned lab actions layer for extraction plates, sequencing library - No real Zebra Day print job was sent. - Bloom release, Dayhoff pin update, and Dayhoff double-release are tracked in `dayhoff/docs/plans/20260618T131619Z_bloom_lab_actions_release_train_ledger.md`. - No live `jemdev5` refresh was performed in this pass. + +## 2026-06-19 Live `jemdev5` Proof + +The lab-action flow was released as Bloom `7.0.19`, pinned by Dayhoff `7.0.74`, +self-pinned by Dayhoff `7.0.75`, and exercised in the live `jemdev5` +greenfield hybrid GUI E2E run. + +| Surface | Live evidence | +|---|---| +| Deploy verification | `/Users/jmajor/projects/mega_dayhoff/dayhoff/output/jemdev5_verify_7_0_75_final/deploy-verify.md` | +| GUI E2E screenshot pack | `/Users/jmajor/projects/mega_dayhoff/dayhoff/output/playwright/hybrid_order_to_delivery/hybrid_order_to_delivery/codex-hybrid-e2e-20260619T002814Z/` | +| Tracked evidence guide | `/Users/jmajor/projects/mega_dayhoff/dayhoff/docs/evidence/hybrid_order_to_delivery_gui_e2e.md` | +| Pytest result | `1 passed in 149.11s` | + +Live Bloom lab-action EUIDs from the passing run: + +| Role | EUID | +|---|---| +| Patient biospecimen tube | `Z-BCT-2AM` | +| Extraction plate | `Z-BCP-19Z` | +| Extraction well | `Z-BCW-3EN3` | +| Extraction gDNA content | `Z-BNG-FX` | +| ILMN prep set | `Z-BGS-15X` | +| ONT prep set | `Z-BGS-16V` | +| ILMN library plate | `Z-BCP-1AX` | +| ONT library plate | `Z-BCP-1BV` | +| ILMN pool tube | `Z-BCT-2BJ` | +| ONT pool tube | `Z-BCT-2CG` | +| ILMN seq-run set | `Z-BGS-17S` | +| ONT seq-run set | `Z-BGS-18Q` | + +All Bloom lab-action rows are source-tested, release-pinned, deployed to +`jemdev5`, and live-proven. Final status: `100%`. From 94f0898f750d3d068eeb6d809bfe921fff23878c Mon Sep 17 00:00:00 2001 From: John Major Date: Fri, 19 Jun 2026 04:08:45 -0700 Subject: [PATCH 43/47] Fix TapDB semantic template categories --- README.md | 4 +- bloom_lims/anomalies.py | 2 +- bloom_lims/auth/repositories/tapdb/groups.py | 4 +- .../repositories/tapdb/user_api_tokens.py | 4 +- bloom_lims/cli/db.py | 20 +- bloom_lims/config/container/tube.json | 8 +- bloom_lims/config/container/well.json | 2 +- bloom_lims/config/workflow_step/analysis.json | 16 +- .../workflow_step/extraction-steps.json | 56 +- bloom_lims/domain/beta_lab.py | 8 +- bloom_lims/domain/execution_queue.py | 32 +- bloom_lims/domain/lab_actions.py | 6 +- bloom_lims/schemas/beta_lab.py | 2 +- bloom_lims/schemas/external_specimens.py | 2 +- config/tapdb_templates/bloom/templates.json | 1342 ++++++++--------- docs/apis.md | 2 +- docs/lab_actions.md | 12 +- docs/old_docs/ATLAS_BLOOM_CONTRACT_TESTS.md | 2 +- docs/old_docs/TAPDB_SPECIFICATION.md | 2 +- ...520T064952Z_bloom_sequencing_run_ledger.md | 4 +- ...18T000000Z_bloom_lab_action_flow_ledger.md | 28 +- pyproject.toml | 2 +- tests/test_atlas_workflow_contract.py | 8 +- tests/test_bloom_prefix_taxonomy.py | 33 +- tests/test_cli_db_command_paths.py | 33 +- tests/test_core_validation.py | 2 +- tests/test_packaging_metadata.py | 2 +- tests/test_sequencing_run_contract.py | 2 +- uv.lock | 6 +- 29 files changed, 833 insertions(+), 813 deletions(-) diff --git a/README.md b/README.md index b337713..24a494e 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,12 @@ Bloom is the LSMC internal material and container graph service. It owns laboratory containers, materials/content, equipment, lineage, recursive template creation, search, and graph views. Atlas owns orders and customer-facing accession context; Bloom owns the physical/material execution graph. -Current Dayhoff pin: `7.0.16`. Current TapDB dependency: `daylily-tapdb @ ...@9.0.5`. +Current Dayhoff pin: `7.0.20`. Current TapDB dependency: `daylily-tapdb @ ...@9.0.6`. Bloom is internal-only in Dayhoff exposure policy. It must be reachable only through approved LSMC networks and Dayhoff-generated service credentials. +TapDB template identity is semantic. Categories are names like `container`, `material`, `data`, `equipment`, `set`, and `workflow`; prefixes such as `BC*`, `BN*`, `BD*`, `BR*`, and `BG*` are issuance and governance labels only. Existing historical objects keep their already minted EUIDs, but new and active template definitions must not use a Meridian/EUID prefix as the category name. + ## What It Does - Owns Bloom containers, materials/content, equipment, recursive templates, lineages, search, and graph views. diff --git a/bloom_lims/anomalies.py b/bloom_lims/anomalies.py index 9ee96d0..9684423 100644 --- a/bloom_lims/anomalies.py +++ b/bloom_lims/anomalies.py @@ -15,7 +15,7 @@ from bloom_lims.config import get_settings from bloom_lims.observability import ProjectionMetadata -ANOMALY_TEMPLATE_CODE = "BAN/ops/anomaly-record/1.0/" +ANOMALY_TEMPLATE_CODE = "ops/anomaly-record/generic/1.0/" ANOMALY_PREFIX = "BAN" diff --git a/bloom_lims/auth/repositories/tapdb/groups.py b/bloom_lims/auth/repositories/tapdb/groups.py index 19a87dd..2c30f5b 100644 --- a/bloom_lims/auth/repositories/tapdb/groups.py +++ b/bloom_lims/auth/repositories/tapdb/groups.py @@ -22,8 +22,8 @@ ) from bloom_lims.config import get_settings -GROUP_TEMPLATE_CODE = "BBX/auth/user-group/1.0/" -GROUP_MEMBERSHIP_TEMPLATE_CODE = "BBX/auth/user-group-membership/1.0/" +GROUP_TEMPLATE_CODE = "auth/user-group/generic/1.0/" +GROUP_MEMBERSHIP_TEMPLATE_CODE = "auth/user-group-membership/generic/1.0/" GROUP_PREFIX = "BBX" GROUP_MEMBERSHIP_PREFIX = "BBX" diff --git a/bloom_lims/auth/repositories/tapdb/user_api_tokens.py b/bloom_lims/auth/repositories/tapdb/user_api_tokens.py index d59d853..49df31f 100644 --- a/bloom_lims/auth/repositories/tapdb/user_api_tokens.py +++ b/bloom_lims/auth/repositories/tapdb/user_api_tokens.py @@ -22,8 +22,8 @@ ) from bloom_lims.config import get_settings -TOKEN_TEMPLATE_CODE = "BBX/auth/user-api-token/1.0/" -TOKEN_USAGE_LOG_TEMPLATE_CODE = "BBX/auth/user-api-token-usage-log/1.0/" +TOKEN_TEMPLATE_CODE = "auth/user-api-token/generic/1.0/" +TOKEN_USAGE_LOG_TEMPLATE_CODE = "auth/user-api-token-usage-log/generic/1.0/" TOKEN_PREFIX = "BBX" TOKEN_USAGE_PREFIX = "BBX" diff --git a/bloom_lims/cli/db.py b/bloom_lims/cli/db.py index 5d3107d..dd8859a 100644 --- a/bloom_lims/cli/db.py +++ b/bloom_lims/cli/db.py @@ -383,7 +383,12 @@ def _retire_obsolete_template_variants( domain_code: str, ) -> int: current = _active_template_categories_by_semantic_key(templates) - if not current: + current_prefixes = { + str(template.get("instance_prefix") or "").strip().upper() + for template in templates + if str(template.get("instance_prefix") or "").strip() + } + if not current and not current_prefixes: return 0 rows = ( @@ -405,9 +410,20 @@ def _retire_obsolete_template_variants( str(row.version or "").strip(), ) expected_category = current.get(key) + actual_category = str(row.category or "").strip().upper() + semantic_category = template_semantic_category(row).strip().upper() + if semantic_category and actual_category != semantic_category: + row.is_deleted = True + row.bstatus = "retired" + retired += 1 + continue + if actual_category in current_prefixes: + row.is_deleted = True + row.bstatus = "retired" + retired += 1 + continue if expected_category is None: continue - actual_category = str(row.category or "").strip().upper() if actual_category == expected_category: continue row.is_deleted = True diff --git a/bloom_lims/config/container/tube.json b/bloom_lims/config/container/tube.json index 7b7fdc4..d38a26b 100644 --- a/bloom_lims/config/container/tube.json +++ b/bloom_lims/config/container/tube.json @@ -39,7 +39,7 @@ "name": "10ml glass tube" }, "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [] } @@ -84,7 +84,7 @@ "name": "1.5ml eppi tube" }, "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [] } @@ -129,7 +129,7 @@ "name": "10ml tube generic" }, "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [] } @@ -175,7 +175,7 @@ }, "singleton": "0", "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [] } diff --git a/bloom_lims/config/container/well.json b/bloom_lims/config/container/well.json index a9d6e1b..77e7f58 100644 --- a/bloom_lims/config/container/well.json +++ b/bloom_lims/config/container/well.json @@ -36,7 +36,7 @@ }, "singleton": "0", "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [ "container/plate/fixed-plate.*?/.*?" diff --git a/bloom_lims/config/workflow_step/analysis.json b/bloom_lims/config/workflow_step/analysis.json index d815f0c..44973f7 100644 --- a/bloom_lims/config/workflow_step/analysis.json +++ b/bloom_lims/config/workflow_step/analysis.json @@ -22,44 +22,44 @@ "expected_inputs": [ { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } } ], "expected_outputs": [ { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } } ], diff --git a/bloom_lims/config/workflow_step/extraction-steps.json b/bloom_lims/config/workflow_step/extraction-steps.json index 925c5e6..ff54b1d 100644 --- a/bloom_lims/config/workflow_step/extraction-steps.json +++ b/bloom_lims/config/workflow_step/extraction-steps.json @@ -24,22 +24,22 @@ [ { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } } ] @@ -51,7 +51,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/cfdna/*" + "holds_content": "material/sample/cfdna/*" } } ] @@ -62,7 +62,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/cfdna/*" + "holds_content": "material/sample/cfdna/*" } } ] @@ -73,7 +73,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/cfdna/*" + "holds_content": "material/sample/cfdna/*" } } ] @@ -84,7 +84,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/cfdna/*" + "holds_content": "material/sample/cfdna/*" } } ] @@ -133,22 +133,22 @@ "expected_inputs": [ { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } } ], @@ -159,7 +159,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/gdna/*" + "holds_content": "material/sample/gdna/*" } } ] @@ -170,7 +170,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/gdna/*" + "holds_content": "material/sample/gdna/*" } } ] @@ -181,7 +181,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/gdna/*" + "holds_content": "material/sample/gdna/*" } } ] @@ -192,7 +192,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/gdna/*" + "holds_content": "material/sample/gdna/*" } } ] @@ -241,22 +241,22 @@ "expected_inputs": [ { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } } ], @@ -302,44 +302,44 @@ "expected_inputs": [ { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } } ], "expected_outputs": [ { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } } ], diff --git a/bloom_lims/domain/beta_lab.py b/bloom_lims/domain/beta_lab.py index 1c1a4b4..3a377ce 100644 --- a/bloom_lims/domain/beta_lab.py +++ b/bloom_lims/domain/beta_lab.py @@ -60,10 +60,10 @@ class BetaLabService( REL_MATERIAL_CONSUMPTION = "beta_material_consumption" REL_USED_INSTRUMENT = "beta_used_instrument" REL_USED_REAGENT = "beta_used_reagent" - POOL_TEMPLATE_CODE = "content/pool/sequencing-library/1.0" + POOL_TEMPLATE_CODE = "material/pool/sequencing-library/1.0" POOL_CONTAINER_TEMPLATE_CODE = "container/tube/tube-generic-10ml/1.0" LIBRARY_PREP_OUTPUT_TEMPLATE_CODE = "data/operation/library-prep/1.0" - LIBRARY_MATERIAL_TEMPLATE_CODE = "content/sample/sequencing-library/1.0" + LIBRARY_MATERIAL_TEMPLATE_CODE = "material/sample/sequencing-library/1.0" SEQUENCING_RUN_TEMPLATE_BY_SUBTYPE = { "illumina": "data/sequencing_run/illumina/1.0", "ont": "data/sequencing_run/ont/1.0", @@ -73,8 +73,8 @@ class BetaLabService( } LIBRARY_PLATE_TEMPLATE_CODE = "container/plate/sequencing-library-plate-96/1.0" EXTRACTION_TEMPLATE_BY_TYPE = { - "cfdna": "content/sample/cfdna/1.0", - "gdna": "content/sample/gdna/1.0", + "cfdna": "material/sample/cfdna/1.0", + "gdna": "material/sample/gdna/1.0", } LIB_PREP_QUEUE_BY_PLATFORM = { "ILMN": "ilmn_lib_prep", diff --git a/bloom_lims/domain/execution_queue.py b/bloom_lims/domain/execution_queue.py index 499603c..bdf0f6e 100644 --- a/bloom_lims/domain/execution_queue.py +++ b/bloom_lims/domain/execution_queue.py @@ -130,7 +130,7 @@ class ExecutionQueueService: "dispatch_priority": 100, "subject_template_codes": [ "container/tube/tube-generic-10ml/1.0", - "content/specimen/blood-whole/1.0", + "material/specimen/blood-whole/1.0", ], "required_worker_capabilities": ["wetlab.extraction"], }, @@ -139,7 +139,7 @@ class ExecutionQueueService: "dispatch_priority": 90, "subject_template_codes": [ "container/tube/tube-generic-10ml/1.0", - "content/specimen/blood-whole/1.0", + "material/specimen/blood-whole/1.0", ], "required_worker_capabilities": ["wetlab.extraction"], }, @@ -147,8 +147,8 @@ class ExecutionQueueService: "display_name": "Post-Extract QC", "dispatch_priority": 100, "subject_template_codes": [ - "content/sample/cfdna/1.0", - "content/sample/gdna/1.0", + "material/sample/cfdna/1.0", + "material/sample/gdna/1.0", ], "required_worker_capabilities": ["wetlab.post_extract_qc"], }, @@ -156,8 +156,8 @@ class ExecutionQueueService: "display_name": "Illumina Library Prep", "dispatch_priority": 100, "subject_template_codes": [ - "content/sample/cfdna/1.0", - "content/sample/gdna/1.0", + "material/sample/cfdna/1.0", + "material/sample/gdna/1.0", ], "required_worker_capabilities": ["wetlab.library_prep", "platform.ILMN"], }, @@ -165,7 +165,7 @@ class ExecutionQueueService: "display_name": "Illumina Library QC", "dispatch_priority": 100, "subject_template_codes": [ - "content/sample/sequencing-library/1.0", + "material/sample/sequencing-library/1.0", ], "required_worker_capabilities": ["wetlab.library_qc", "platform.ILMN"], }, @@ -173,8 +173,8 @@ class ExecutionQueueService: "display_name": "ONT Library Prep", "dispatch_priority": 100, "subject_template_codes": [ - "content/sample/cfdna/1.0", - "content/sample/gdna/1.0", + "material/sample/cfdna/1.0", + "material/sample/gdna/1.0", ], "required_worker_capabilities": ["wetlab.library_prep", "platform.ONT"], }, @@ -183,7 +183,7 @@ class ExecutionQueueService: "dispatch_priority": 100, "subject_template_codes": [ "data/operation/library-prep/1.0", - "content/sample/sequencing-library/1.0", + "material/sample/sequencing-library/1.0", ], "required_worker_capabilities": ["wetlab.pooling", "platform.ILMN"], }, @@ -192,28 +192,28 @@ class ExecutionQueueService: "dispatch_priority": 100, "subject_template_codes": [ "data/operation/library-prep/1.0", - "content/sample/sequencing-library/1.0", + "material/sample/sequencing-library/1.0", ], "required_worker_capabilities": ["wetlab.pooling", "platform.ONT"], }, "ilmn_start_seq_run": { "display_name": "Illumina Start Run", "dispatch_priority": 100, - "subject_template_codes": ["content/pool/sequencing-library/1.0"], + "subject_template_codes": ["material/pool/sequencing-library/1.0"], "required_worker_capabilities": ["wetlab.run_start", "platform.ILMN"], }, "ont_start_seq_run": { "display_name": "ONT Start Run", "dispatch_priority": 100, - "subject_template_codes": ["content/pool/sequencing-library/1.0"], + "subject_template_codes": ["material/pool/sequencing-library/1.0"], "required_worker_capabilities": ["wetlab.run_start", "platform.ONT"], }, "post_extract_exception": { "display_name": "Post-Extract QC Exception", "dispatch_priority": 10, "subject_template_codes": [ - "content/sample/cfdna/1.0", - "content/sample/gdna/1.0", + "material/sample/cfdna/1.0", + "material/sample/gdna/1.0", ], "required_worker_capabilities": ["wetlab.exception_review"], }, @@ -221,7 +221,7 @@ class ExecutionQueueService: "display_name": "Illumina Library QC Exception", "dispatch_priority": 10, "subject_template_codes": [ - "content/sample/sequencing-library/1.0", + "material/sample/sequencing-library/1.0", ], "required_worker_capabilities": [ "wetlab.exception_review", diff --git a/bloom_lims/domain/lab_actions.py b/bloom_lims/domain/lab_actions.py index 99fcbdc..6adb276 100644 --- a/bloom_lims/domain/lab_actions.py +++ b/bloom_lims/domain/lab_actions.py @@ -40,10 +40,10 @@ RUN_SET_TEMPLATE_CODE = "set/run-set/generic/1.01/" EXTRACTION_PLATE_TEMPLATE_CODE = "container/plate/fixed-plate-96/1.0/" LIBRARY_PLATE_TEMPLATE_CODE = "container/plate/sequencing-library-plate-96/1.0/" -GDNA_TEMPLATE_CODE = "content/sample/gdna/1.0/" -LIBRARY_CONTENT_TEMPLATE_CODE = "content/sample/sequencing-library/1.0/" +GDNA_TEMPLATE_CODE = "material/sample/gdna/1.0/" +LIBRARY_CONTENT_TEMPLATE_CODE = "material/sample/sequencing-library/1.0/" POOL_TUBE_TEMPLATE_CODE = "container/tube/tube-generic-10ml/1.0/" -POOL_CONTENT_TEMPLATE_CODE = "content/pool/sequencing-library/1.0/" +POOL_CONTENT_TEMPLATE_CODE = "material/pool/sequencing-library/1.0/" GDNA_QUANT_TEMPLATE_CODE = "data/quantification/gdna/1.0/" EXTRACTION_QC_TEMPLATE_CODE = "data/operation/extraction-qc/1.0/" SEQUENCED_LIBRARY_ASSIGNMENT_TEMPLATE_CODE = ( diff --git a/bloom_lims/schemas/beta_lab.py b/bloom_lims/schemas/beta_lab.py index cf123ae..1e855ea 100644 --- a/bloom_lims/schemas/beta_lab.py +++ b/bloom_lims/schemas/beta_lab.py @@ -174,7 +174,7 @@ def validate_context(self) -> "AtlasFulfillmentContext": class BetaAcceptedMaterialCreateRequest(BaseModel): - specimen_template_code: str = Field(default="content/specimen/blood-whole/1.0") + specimen_template_code: str = Field(default="material/specimen/blood-whole/1.0") specimen_name: str | None = None container_euid: str | None = None container_template_code: str = Field(default="container/tube/tube-generic-10ml/1.0") diff --git a/bloom_lims/schemas/external_specimens.py b/bloom_lims/schemas/external_specimens.py index df3a009..041c7be 100644 --- a/bloom_lims/schemas/external_specimens.py +++ b/bloom_lims/schemas/external_specimens.py @@ -20,7 +20,7 @@ class AtlasReferences(BaseModel): class ExternalSpecimenCreateRequest(BaseModel): - specimen_template_code: str = Field(default="content/specimen/generic/1.0") + specimen_template_code: str = Field(default="material/specimen/generic/1.0") specimen_name: str | None = None container_euid: str | None = None container_template_code: str = Field(default="container/tube/generic/1.0") diff --git a/config/tapdb_templates/bloom/templates.json b/config/tapdb_templates/bloom/templates.json index be5aabf..77d6319 100644 --- a/config/tapdb_templates/bloom/templates.json +++ b/config/tapdb_templates/bloom/templates.json @@ -4,7 +4,7 @@ { "name": "Claim Material In Queue", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "claim_material_in_queue", "version": "1.0", @@ -27,7 +27,7 @@ { "name": "Consume Material", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "consume_material", "version": "1.0", @@ -50,7 +50,7 @@ { "name": "Create Empty Tube", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "create_empty_tube", "version": "1.0", @@ -73,7 +73,7 @@ { "name": "Create Extraction", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "create_extraction", "version": "1.0", @@ -96,7 +96,7 @@ { "name": "Create Library Prep", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "create_library_prep", "version": "1.0", @@ -119,7 +119,7 @@ { "name": "Create Pool", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "create_pool", "version": "1.0", @@ -142,7 +142,7 @@ { "name": "Record Library QC", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "record_library_qc", "version": "1.0", @@ -165,7 +165,7 @@ { "name": "Create Run", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "create_run", "version": "1.0", @@ -188,7 +188,7 @@ { "name": "Move Material To Queue", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "move_material_to_queue", "version": "1.0", @@ -211,7 +211,7 @@ { "name": "Record Post Extract QC", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "record_post_extract_qc", "version": "1.0", @@ -234,7 +234,7 @@ { "name": "Register Accepted Material", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "register_accepted_material", "version": "1.0", @@ -257,7 +257,7 @@ { "name": "Release Claim", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "release_claim", "version": "1.0", @@ -280,7 +280,7 @@ { "name": "Release Reservation", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "release_reservation", "version": "1.0", @@ -303,7 +303,7 @@ { "name": "Reserve Material", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "reserve_material", "version": "1.0", @@ -326,7 +326,7 @@ { "name": "Update Specimen", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "update_specimen", "version": "1.0", @@ -349,7 +349,7 @@ { "name": "Update Tube", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "update_tube", "version": "1.0", @@ -372,7 +372,7 @@ { "name": "action/core/add-relationships/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "core", "subtype": "add-relationships", "version": "1.0", @@ -408,7 +408,7 @@ { "name": "action/core/create-subject-and-anchor/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "core", "subtype": "create-subject-and-anchor", "version": "1.0", @@ -444,7 +444,7 @@ { "name": "action/core/print_barcode_label/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "core", "subtype": "print_barcode_label", "version": "1.0", @@ -488,7 +488,7 @@ { "name": "action/core/set_object_status/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "core", "subtype": "set_object_status", "version": "1.0", @@ -528,7 +528,7 @@ { "name": "action/data/log-temperature/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "data", "subtype": "log-temperature", "version": "1.0", @@ -571,7 +571,7 @@ { "name": "action/data/record_cfdna_quant_outcome/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "data", "subtype": "record_cfdna_quant_outcome", "version": "1.0", @@ -614,7 +614,7 @@ { "name": "action/equipment/add-container-to/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "equipment", "subtype": "add-container-to", "version": "1.0", @@ -661,7 +661,7 @@ { "name": "action/equipment/download-inventory-tsv/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "equipment", "subtype": "download-inventory-tsv", "version": "1.0", @@ -707,7 +707,7 @@ { "name": "action/equipment/log-temperature/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "equipment", "subtype": "log-temperature", "version": "1.0", @@ -760,7 +760,7 @@ { "name": "action/equipment/remove-container-from/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "equipment", "subtype": "remove-container-from", "version": "1.0", @@ -806,7 +806,7 @@ { "name": "Cancel Subject Execution", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "cancel_subject_execution", "version": "1.0", @@ -829,7 +829,7 @@ { "name": "Claim Queue Item", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "claim_queue_item", "version": "1.0", @@ -852,7 +852,7 @@ { "name": "Complete Queue Execution", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "complete_queue_execution", "version": "1.0", @@ -875,7 +875,7 @@ { "name": "Expire Queue Lease", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "expire_queue_lease", "version": "1.0", @@ -898,7 +898,7 @@ { "name": "Fail Queue Execution", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "fail_queue_execution", "version": "1.0", @@ -921,7 +921,7 @@ { "name": "Heartbeat Worker", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "heartbeat_worker", "version": "1.0", @@ -944,7 +944,7 @@ { "name": "Place Execution Hold", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "place_execution_hold", "version": "1.0", @@ -967,7 +967,7 @@ { "name": "Register Worker", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "register_worker", "version": "1.0", @@ -990,7 +990,7 @@ { "name": "Release Execution Hold", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "release_execution_hold", "version": "1.0", @@ -1013,7 +1013,7 @@ { "name": "Release Queue Lease", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "release_queue_lease", "version": "1.0", @@ -1036,7 +1036,7 @@ { "name": "Renew Queue Lease", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "renew_queue_lease", "version": "1.0", @@ -1059,7 +1059,7 @@ { "name": "Requeue Subject", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "execution_queue", "subtype": "requeue_subject", "version": "1.0", @@ -1082,7 +1082,7 @@ { "name": "action/file/download_file/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "file", "subtype": "download_file", "version": "1.0", @@ -1118,7 +1118,7 @@ { "name": "action/move-queues/move-among-ay-top-queues/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "move-queues", "subtype": "move-among-ay-top-queues", "version": "1.0", @@ -1162,7 +1162,7 @@ { "name": "action/object/set-child-object/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "object", "subtype": "set-child-object", "version": "1.0", @@ -1210,7 +1210,7 @@ { "name": "action/plate/download_plate_map/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "plate", "subtype": "download_plate_map", "version": "1.0", @@ -1255,7 +1255,7 @@ { "name": "action/plate/stamp_plates_into_plate/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "plate", "subtype": "stamp_plates_into_plate", "version": "1.0", @@ -1313,7 +1313,7 @@ { "name": "action/plate/stamp_plates_into_plate__extraction_plate_filled/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "plate", "subtype": "stamp_plates_into_plate__extraction_plate_filled", "version": "1.0", @@ -1371,7 +1371,7 @@ { "name": "action/pool/create-pool-from-containers/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "pool", "subtype": "create-pool-from-containers", "version": "1.0", @@ -1438,7 +1438,7 @@ { "name": "action/pool/create_pool_from_containers__extraction_plate_filled/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "pool", "subtype": "create_pool_from_containers__extraction_plate_filled", "version": "1.0", @@ -1505,7 +1505,7 @@ { "name": "action/workflow/destroy_specimen_containers/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow", "subtype": "destroy_specimen_containers", "version": "1.0", @@ -1559,7 +1559,7 @@ { "name": "action/workflow_step_analysis/download_quant_data/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_analysis", "subtype": "download_quant_data", "version": "1.0", @@ -1605,7 +1605,7 @@ { "name": "action/workflow_step_destroy/complete_specimen_destroy/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_destroy", "subtype": "complete_specimen_destroy", "version": "1.0", @@ -1649,7 +1649,7 @@ { "name": "action/workflow_step_extraction/cfdna_post_extraction_quant/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_extraction", "subtype": "cfdna_post_extraction_quant", "version": "1.0", @@ -1711,7 +1711,7 @@ { "name": "action/workflow_step_plate_operations/cfdna_quant/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_plate_operations", "subtype": "cfdna_quant", "version": "1.0", @@ -1773,7 +1773,7 @@ { "name": "action/workflow_step_plate_operations/stamp_copy_plate/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_plate_operations", "subtype": "stamp_copy_plate", "version": "1.0", @@ -1826,7 +1826,7 @@ { "name": "action/workflow_step_queue/add-gdna-to-gdna-normalization-queue/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "add-gdna-to-gdna-normalization-queue", "version": "1.0", @@ -1869,7 +1869,7 @@ { "name": "action/workflow_step_queue/add-to-blood-gdna-extraction-queue/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "add-to-blood-gdna-extraction-queue", "version": "1.0", @@ -1912,7 +1912,7 @@ { "name": "action/workflow_step_queue/add-to-buccal-gdna-extraction-queue/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "add-to-buccal-gdna-extraction-queue", "version": "1.0", @@ -1955,7 +1955,7 @@ { "name": "action/workflow_step_queue/create_test_req_and_link_child_workflow_step/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "create_test_req_and_link_child_workflow_step", "version": "1.0", @@ -2019,7 +2019,7 @@ { "name": "action/workflow_step_queue/existing-plate-fill-auto/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "existing-plate-fill-auto", "version": "1.0", @@ -2061,7 +2061,7 @@ { "name": "action/workflow_step_queue/existing-plate-fill-directed/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "existing-plate-fill-directed", "version": "1.0", @@ -2103,7 +2103,7 @@ { "name": "action/workflow_step_queue/fill_plate_directed/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "fill_plate_directed", "version": "1.0", @@ -2152,7 +2152,7 @@ { "name": "action/workflow_step_queue/fill_plate_undirected/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "fill_plate_undirected", "version": "1.0", @@ -2205,7 +2205,7 @@ { "name": "action/workflow_step_queue/link_tubes_auto/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "link_tubes_auto", "version": "1.0", @@ -2276,7 +2276,7 @@ { "name": "action/workflow_step_queue/plate-create-fill-auto/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "plate-create-fill-auto", "version": "1.0", @@ -2318,7 +2318,7 @@ { "name": "action/workflow_step_queue/plate-create-fill-directed/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "plate-create-fill-directed", "version": "1.0", @@ -2360,7 +2360,7 @@ { "name": "action/workflow_step_queue/save-quant-data/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "save-quant-data", "version": "1.0", @@ -2402,7 +2402,7 @@ { "name": "action/workflow_step_queue/ycreate_test_req_and_link_child_workflow_step/1.0", "polymorphic_discriminator": "action_template", - "category": "BAC", + "category": "action", "type": "workflow_step_queue", "subtype": "ycreate_test_req_and_link_child_workflow_step", "version": "1.0", @@ -2464,7 +2464,7 @@ { "name": "Bloom Authenticated User", "polymorphic_discriminator": "actor_template", - "category": "BAR", + "category": "actor", "type": "generic", "subtype": "bloom-user", "version": "1.0", @@ -2475,10 +2475,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Bloom Authenticated User", "expected_inputs": [], @@ -2497,7 +2497,7 @@ { "name": "Patient", "polymorphic_discriminator": "actor_template", - "category": "BAR", + "category": "actor", "type": "generic", "subtype": "patient", "version": "1.0", @@ -2508,10 +2508,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Patient", "expected_inputs": [], @@ -2534,7 +2534,7 @@ { "name": "Clinician", "polymorphic_discriminator": "actor_template", - "category": "BAR", + "category": "actor", "type": "generic", "subtype": "physician", "version": "1.0", @@ -2545,10 +2545,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -2581,7 +2581,7 @@ { "name": "Execution System Worker", "polymorphic_discriminator": "actor_template", - "category": "BAR", + "category": "actor", "type": "system", "subtype": "worker", "version": "1.0", @@ -2592,9 +2592,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Execution System Worker", "expected_inputs": [], @@ -2628,7 +2628,7 @@ { "name": "Bloom User", "polymorphic_discriminator": "generic_template", - "category": "BAR", + "category": "actor", "type": "user", "subtype": "bloom-user", "version": "1.0", @@ -2653,10 +2653,10 @@ "group_order": "1", "group_name": "Core Actions", "actions": { - "BAC/core/add-relationships/1.0": {}, - "BAC/core/create-subject-and-anchor/1.0": {}, - "BAC/core/print_barcode_label/1.0": {}, - "BAC/core/set_object_status/1.0": {} + "action/core/add-relationships/1.0": {}, + "action/core/create-subject-and-anchor/1.0": {}, + "action/core/print_barcode_label/1.0": {}, + "action/core/set_object_status/1.0": {} } } }, @@ -2667,9 +2667,9 @@ { "name": "Bloom User API Token Usage Log", "polymorphic_discriminator": "generic_template", - "category": "BBX", - "type": "auth", - "subtype": "user-api-token-usage-log", + "category": "auth", + "type": "user-api-token-usage-log", + "subtype": "generic", "version": "1.0", "instance_prefix": "BBX", "instance_polymorphic_identity": "generic_instance", @@ -2684,9 +2684,9 @@ { "name": "Bloom User API Token", "polymorphic_discriminator": "generic_template", - "category": "BBX", - "type": "auth", - "subtype": "user-api-token", + "category": "auth", + "type": "user-api-token", + "subtype": "generic", "version": "1.0", "instance_prefix": "BBX", "instance_polymorphic_identity": "generic_instance", @@ -2701,7 +2701,7 @@ { "name": "Generic External Object Link", "polymorphic_discriminator": "generic_template", - "category": "BGX", + "category": "generic", "type": "generic", "subtype": "external_object_link", "version": "1.0", @@ -2725,9 +2725,9 @@ { "name": "Bloom User Group Membership", "polymorphic_discriminator": "generic_template", - "category": "BBX", - "type": "auth", - "subtype": "user-group-membership", + "category": "auth", + "type": "user-group-membership", + "subtype": "generic", "version": "1.0", "instance_prefix": "BBX", "instance_polymorphic_identity": "generic_instance", @@ -2742,9 +2742,9 @@ { "name": "Bloom User Group", "polymorphic_discriminator": "generic_template", - "category": "BBX", - "type": "auth", - "subtype": "user-group", + "category": "auth", + "type": "user-group", + "subtype": "generic", "version": "1.0", "instance_prefix": "BBX", "instance_polymorphic_identity": "generic_instance", @@ -2759,7 +2759,7 @@ { "name": "24 fixed well plate", "polymorphic_discriminator": "container_template", - "category": "BCP", + "category": "container", "type": "plate", "subtype": "fixed-plate-24", "version": "1.0", @@ -2770,12 +2770,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "download_plate_map": "BAC/plate/download_plate_map/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0", - "stamp_plates_into_plate": "BAC/plate/stamp_plates_into_plate/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "download_plate_map": "action/plate/download_plate_map/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0", + "stamp_plates_into_plate": "action/plate/stamp_plates_into_plate/1.0" }, "cogs": { "allocation_type": "", @@ -3300,7 +3300,7 @@ { "name": "96 well fixed plate", "polymorphic_discriminator": "container_template", - "category": "BCP", + "category": "container", "type": "plate", "subtype": "fixed-plate-96", "version": "1.0", @@ -3311,12 +3311,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "download_plate_map": "BAC/plate/download_plate_map/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0", - "stamp_plates_into_plate": "BAC/plate/stamp_plates_into_plate/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "download_plate_map": "action/plate/download_plate_map/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0", + "stamp_plates_into_plate": "action/plate/stamp_plates_into_plate/1.0" }, "cogs": { "allocation_type": "", @@ -4934,7 +4934,7 @@ { "name": "4 position Rack", "polymorphic_discriminator": "container_template", - "category": "BCR", + "category": "container", "type": "rack", "subtype": "tube-rack-4-empty", "version": "1.0", @@ -4945,10 +4945,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -4987,7 +4987,7 @@ { "name": "4 position Rack", "polymorphic_discriminator": "container_template", - "category": "BCR", + "category": "container", "type": "rack", "subtype": "tube-rack-generic", "version": "1.0", @@ -4998,10 +4998,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5094,7 +5094,7 @@ { "name": "Shelf", "polymorphic_discriminator": "container_template", - "category": "BCR", + "category": "container", "type": "shelf", "subtype": "generic", "version": "1.0", @@ -5105,11 +5105,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_container_to": "BAC/equipment/add-container-to/1.0", - "download_inventory_tsv": "BAC/equipment/download-inventory-tsv/1.0", - "log_temperature": "BAC/equipment/log-temperature/1.0", - "remove_container_from": "BAC/equipment/remove-container-from/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_container_to": "action/equipment/add-container-to/1.0", + "download_inventory_tsv": "action/equipment/download-inventory-tsv/1.0", + "log_temperature": "action/equipment/log-temperature/1.0", + "remove_container_from": "action/equipment/remove-container-from/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5150,7 +5150,7 @@ { "name": "10ml glass tube", "polymorphic_discriminator": "container_template", - "category": "BCT", + "category": "container", "type": "tube", "subtype": "tube-10ml-glass", "version": "1.0", @@ -5161,10 +5161,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5197,7 +5197,7 @@ "name": "10ml glass tube" }, "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [], "semantic_category": "container" @@ -5206,7 +5206,7 @@ { "name": "1.5ml eppi tube", "polymorphic_discriminator": "container_template", - "category": "BCT", + "category": "container", "type": "tube", "subtype": "tube-eppi-1.5ml", "version": "1.0", @@ -5217,10 +5217,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5253,7 +5253,7 @@ "name": "1.5ml eppi tube" }, "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [], "semantic_category": "container" @@ -5262,7 +5262,7 @@ { "name": "Generic 10ml tube", "polymorphic_discriminator": "container_template", - "category": "BCT", + "category": "container", "type": "tube", "subtype": "tube-generic-10ml", "version": "1.0", @@ -5273,10 +5273,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5309,7 +5309,7 @@ "name": "10ml tube generic" }, "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [], "semantic_category": "container" @@ -5318,7 +5318,7 @@ { "name": "strek 10ml", "polymorphic_discriminator": "container_template", - "category": "BCT", + "category": "container", "type": "tube", "subtype": "tube-strek-10ml", "version": "1.0", @@ -5329,10 +5329,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5366,7 +5366,7 @@ }, "singleton": "0", "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [], "semantic_category": "container" @@ -5375,7 +5375,7 @@ { "name": "Mawi DNA Technologies - ISD-T-250 - iSWAB DNA Collection Tube - Buccal", "polymorphic_discriminator": "container_template", - "category": "BCT", + "category": "container", "type": "tube", "subtype": "mawi-isd-t-250-iswab-dna-collection-tube-buccal", "version": "1.0", @@ -5386,10 +5386,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5426,7 +5426,7 @@ }, "singleton": "0", "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [], "semantic_category": "container" @@ -5435,7 +5435,7 @@ { "name": "plate well - fixed", "polymorphic_discriminator": "container_template", - "category": "BCW", + "category": "container", "type": "well", "subtype": "fixed-plate-well", "version": "1.0", @@ -5446,7 +5446,7 @@ "json_addl": { "action_groups": {}, "action_imports": { - "set_object_status": "BAC/core/set_object_status/1.0" + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5479,7 +5479,7 @@ }, "singleton": "0", "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [ "container/plate/fixed-plate.*?/.*?" @@ -5490,7 +5490,7 @@ { "name": "giab NA12878", "polymorphic_discriminator": "content_template", - "category": "BNK", + "category": "material", "type": "control", "subtype": "giab-HG001", "version": "1.0", @@ -5501,10 +5501,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5534,7 +5534,7 @@ { "name": "giab NA24385", "polymorphic_discriminator": "content_template", - "category": "BNK", + "category": "material", "type": "control", "subtype": "giab-HG002", "version": "1.0", @@ -5545,10 +5545,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5578,7 +5578,7 @@ { "name": "synthetic-cfdna", "polymorphic_discriminator": "content_template", - "category": "BNK", + "category": "material", "type": "control", "subtype": "synthetic-cfdna", "version": "1.0", @@ -5589,10 +5589,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5622,7 +5622,7 @@ { "name": "NTC - water", "polymorphic_discriminator": "content_template", - "category": "BNK", + "category": "material", "type": "control", "subtype": "water-ntc", "version": "1.0", @@ -5633,10 +5633,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -5666,7 +5666,7 @@ { "name": "Pool of Contents", "polymorphic_discriminator": "content_template", - "category": "BNP", + "category": "material", "type": "pool", "subtype": "generic", "version": "1.0", @@ -5702,7 +5702,7 @@ { "name": "Novaseq sequencing kit", "polymorphic_discriminator": "content_template", - "category": "BNR", + "category": "material", "type": "reagent", "subtype": "ILMN-nova-seq-kit", "version": "1.0", @@ -5745,7 +5745,7 @@ { "name": "idt probes for rare mendelian disease, v1.0", "polymorphic_discriminator": "content_template", - "category": "BNR", + "category": "material", "type": "reagent", "subtype": "idt-probes-rare-mendelian", "version": "1.0", @@ -5788,7 +5788,7 @@ { "name": "NaOH", "polymorphic_discriminator": "content_template", - "category": "BNR", + "category": "material", "type": "reagent", "subtype": "naoh", "version": "1.0", @@ -5832,7 +5832,7 @@ { "name": "plasma (whole blood)", "polymorphic_discriminator": "content_template", - "category": "BNB", + "category": "material", "type": "sample", "subtype": "blood-plasma", "version": "1.0", @@ -5870,7 +5870,7 @@ { "name": "cell free DNA ", "polymorphic_discriminator": "content_template", - "category": "BNC", + "category": "material", "type": "sample", "subtype": "cfdna", "version": "1.0", @@ -5908,7 +5908,7 @@ { "name": "genomic DNA", "polymorphic_discriminator": "content_template", - "category": "BNG", + "category": "material", "type": "sample", "subtype": "gdna", "version": "1.0", @@ -5946,7 +5946,7 @@ { "name": "Whole Blood", "polymorphic_discriminator": "content_template", - "category": "BNB", + "category": "material", "type": "specimen", "subtype": "blood-whole", "version": "1.0", @@ -5984,7 +5984,7 @@ { "name": "Buccal Swab", "polymorphic_discriminator": "content_template", - "category": "BNS", + "category": "material", "type": "specimen", "subtype": "buccal-swab", "version": "1.0", @@ -6022,7 +6022,7 @@ { "name": "FFPE Block", "polymorphic_discriminator": "content_template", - "category": "BNF", + "category": "material", "type": "specimen", "subtype": "ffpe-block", "version": "1.0", @@ -6060,7 +6060,7 @@ { "name": "FFPE Slides", "polymorphic_discriminator": "content_template", - "category": "BNF", + "category": "material", "type": "specimen", "subtype": "ffpe-slice", "version": "1.0", @@ -6098,7 +6098,7 @@ { "name": "Execution Dead Letter Record", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "data", "type": "execution", "subtype": "dead_letter", "version": "1.0", @@ -6109,9 +6109,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Execution Dead Letter Record", "expected_inputs": [], @@ -6137,7 +6137,7 @@ { "name": "Execution Attempt Record", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "data", "type": "execution", "subtype": "execution_record", "version": "1.0", @@ -6148,9 +6148,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Execution Attempt Record", "expected_inputs": [], @@ -6190,7 +6190,7 @@ { "name": "Execution Hold", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "data", "type": "execution", "subtype": "hold", "version": "1.0", @@ -6201,9 +6201,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Execution Hold", "expected_inputs": [], @@ -6227,7 +6227,7 @@ { "name": "Execution Queue Definition", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "data", "type": "execution", "subtype": "queue", "version": "1.0", @@ -6238,10 +6238,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Execution Queue Definition", "expected_inputs": [], @@ -6291,7 +6291,7 @@ { "name": "Execution Queue Lease", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "data", "type": "execution", "subtype": "queue_lease", "version": "1.0", @@ -6302,9 +6302,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Execution Queue Lease", "expected_inputs": [], @@ -6335,7 +6335,7 @@ { "name": "Generic Data Record", "polymorphic_discriminator": "data_template", - "category": "BD", + "category": "data", "type": "generic", "subtype": "generic", "version": "1.0", @@ -6346,9 +6346,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Generic data record", "expected_inputs": [], @@ -6365,7 +6365,7 @@ { "name": "DNA Quant Data", "polymorphic_discriminator": "data_template", - "category": "BDQ", + "category": "data", "type": "generic", "subtype": "gdna-quantification", "version": "1.0", @@ -6376,11 +6376,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "record_cfdna_quant_outcome": "BAC/data/record_cfdna_quant_outcome/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "record_cfdna_quant_outcome": "action/data/record_cfdna_quant_outcome/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -6408,7 +6408,7 @@ { "name": "Temperature Log Data", "polymorphic_discriminator": "data_template", - "category": "BDC", + "category": "data", "type": "generic", "subtype": "temperature-log", "version": "1.0", @@ -6445,7 +6445,7 @@ { "name": "Wet-Lab Library Prep Output", "polymorphic_discriminator": "data_template", - "category": "BDP", + "category": "data", "type": "wetlab", "subtype": "library_prep_output", "version": "1.0", @@ -6456,10 +6456,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Wet-Lab Library Prep Output", "expected_inputs": [], @@ -6478,7 +6478,7 @@ { "name": "Illumina Sequencing Run", "polymorphic_discriminator": "data_template", - "category": "BRM", + "category": "data", "type": "sequencing_run", "subtype": "illumina", "version": "1.0", @@ -6489,9 +6489,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Sequencing run executed on an Illumina instrument.", "expected_inputs": [], @@ -6516,7 +6516,7 @@ { "name": "ONT Sequencing Run", "polymorphic_discriminator": "data_template", - "category": "BRN", + "category": "data", "type": "sequencing_run", "subtype": "ont", "version": "1.0", @@ -6527,9 +6527,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Sequencing run executed on an Oxford Nanopore instrument.", "expected_inputs": [], @@ -6554,7 +6554,7 @@ { "name": "Ultima Sequencing Run", "polymorphic_discriminator": "data_template", - "category": "BRT", + "category": "data", "type": "sequencing_run", "subtype": "ultima", "version": "1.0", @@ -6565,9 +6565,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Sequencing run executed on an Ultima instrument.", "expected_inputs": [], @@ -6592,7 +6592,7 @@ { "name": "CompleteGenomics Sequencing Run", "polymorphic_discriminator": "data_template", - "category": "BRC", + "category": "data", "type": "sequencing_run", "subtype": "completegenomics", "version": "1.0", @@ -6603,9 +6603,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Sequencing run executed on a Complete Genomics instrument.", "expected_inputs": [], @@ -6630,7 +6630,7 @@ { "name": "PacBio Sequencing Run", "polymorphic_discriminator": "data_template", - "category": "BRP", + "category": "data", "type": "sequencing_run", "subtype": "pacbio", "version": "1.0", @@ -6641,9 +6641,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Sequencing run executed on a PacBio instrument.", "expected_inputs": [], @@ -6668,7 +6668,7 @@ { "name": "-20C Freezer", "polymorphic_discriminator": "equipment_template", - "category": "BEQ", + "category": "equipment", "type": "freezer", "subtype": "freezer-m20c", "version": "1.0", @@ -6679,14 +6679,14 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_container_to": "BAC/equipment/add-container-to/1.0", - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "download_inventory_tsv": "BAC/equipment/download-inventory-tsv/1.0", - "log_temperature": "BAC/equipment/log-temperature/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "remove_container_from": "BAC/equipment/remove-container-from/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_container_to": "action/equipment/add-container-to/1.0", + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "download_inventory_tsv": "action/equipment/download-inventory-tsv/1.0", + "log_temperature": "action/equipment/log-temperature/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "remove_container_from": "action/equipment/remove-container-from/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -6706,7 +6706,7 @@ "relationship_type": "contains", "child_templates": [ { - "template_code": "BCR/shelf/generic/1.0", + "template_code": "container/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6719,7 +6719,7 @@ } }, { - "template_code": "BCR/shelf/generic/1.0", + "template_code": "container/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6750,7 +6750,7 @@ { "name": "-80C Freezer.", "polymorphic_discriminator": "equipment_template", - "category": "BEQ", + "category": "equipment", "type": "freezer", "subtype": "freezer-m80c", "version": "1.0", @@ -6761,14 +6761,14 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_container_to": "BAC/equipment/add-container-to/1.0", - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "download_inventory_tsv": "BAC/equipment/download-inventory-tsv/1.0", - "log_temperature": "BAC/equipment/log-temperature/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "remove_container_from": "BAC/equipment/remove-container-from/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_container_to": "action/equipment/add-container-to/1.0", + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "download_inventory_tsv": "action/equipment/download-inventory-tsv/1.0", + "log_temperature": "action/equipment/log-temperature/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "remove_container_from": "action/equipment/remove-container-from/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -6788,7 +6788,7 @@ "relationship_type": "contains", "child_templates": [ { - "template_code": "BCR/shelf/generic/1.0", + "template_code": "container/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6801,7 +6801,7 @@ } }, { - "template_code": "BCR/shelf/generic/1.0", + "template_code": "container/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6814,7 +6814,7 @@ } }, { - "template_code": "BCR/shelf/generic/1.0", + "template_code": "container/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6827,7 +6827,7 @@ } }, { - "template_code": "BCR/shelf/generic/1.0", + "template_code": "container/shelf/generic/1.0", "json_addl": { "cont_address": { "capacity": "10", @@ -6858,7 +6858,7 @@ { "name": "Bravo Liquid Handler", "polymorphic_discriminator": "equipment_template", - "category": "BEQ", + "category": "equipment", "type": "liquid_handler", "subtype": "bravo", "version": "1.0", @@ -6869,10 +6869,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -6904,7 +6904,7 @@ { "name": "Illumnina MiSeq y", "polymorphic_discriminator": "equipment_template", - "category": "BEQ", + "category": "equipment", "type": "sequencers", "subtype": "miseq-y", "version": "1.0", @@ -6915,10 +6915,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -6950,7 +6950,7 @@ { "name": "Illumnina Novaseq 6000", "polymorphic_discriminator": "equipment_template", - "category": "BEQ", + "category": "equipment", "type": "sequencers", "subtype": "novaseq-6000", "version": "1.0", @@ -6961,10 +6961,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -6996,7 +6996,7 @@ { "name": "ONT MinION", "polymorphic_discriminator": "equipment_template", - "category": "BEQ", + "category": "equipment", "type": "sequencers", "subtype": "ont-minion", "version": "1.0", @@ -7007,10 +7007,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -7042,7 +7042,7 @@ { "name": "Generic File", "polymorphic_discriminator": "file_template", - "category": "BFX", + "category": "file", "type": "file", "subtype": "generic", "version": "1.0", @@ -7053,11 +7053,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "download_file": "BAC/file/download_file/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "download_file": "action/file/download_file/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "arbitrary_association_strings": [], "cogs": { @@ -7375,7 +7375,7 @@ { "name": "Generic File Set", "polymorphic_discriminator": "file_template", - "category": "BFX", + "category": "file", "type": "file_set", "subtype": "generic", "version": "1.0", @@ -7386,10 +7386,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -7469,7 +7469,7 @@ { "name": "Generic Run Set", "polymorphic_discriminator": "generic_template", - "category": "BGS", + "category": "set", "type": "run-set", "subtype": "generic", "version": "1.01", @@ -7480,8 +7480,8 @@ "json_addl": { "action_groups": {}, "action_imports": { - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "controlled_properties": {}, "description": "Generic run set for temporary Bloom lab batch and sequencing-run grouping.", @@ -7570,7 +7570,7 @@ { "name": "Generic File Reference", "polymorphic_discriminator": "file_template", - "category": "BFX", + "category": "file", "type": "shared_ref", "subtype": "generic", "version": "1.0", @@ -7581,10 +7581,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "arbitrary_association_strings": [], "cogs": { @@ -7656,7 +7656,7 @@ { "name": "Generic Health Event", "polymorphic_discriminator": "health_event_template", - "category": "BHE", + "category": "health", "type": "generic", "subtype": "health-event", "version": "1.0", @@ -7667,10 +7667,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -7698,7 +7698,7 @@ { "name": "Decision scope for an accession (ACC / TRX / intake bundle).", "polymorphic_discriminator": "subject_template", - "category": "BSJ", + "category": "subject", "type": "generic", "subtype": "accession-subject", "version": "1.0", @@ -7709,10 +7709,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -7797,7 +7797,7 @@ { "name": "Decision scope for analysis result bundles.", "polymorphic_discriminator": "subject_template", - "category": "BSJ", + "category": "subject", "type": "generic", "subtype": "analysis-bundle-subject", "version": "1.0", @@ -7808,10 +7808,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -7896,7 +7896,7 @@ { "name": "Fallback decision scope for custom use cases.", "polymorphic_discriminator": "subject_template", - "category": "BSJ", + "category": "subject", "type": "generic", "subtype": "generic-subject", "version": "1.0", @@ -7907,10 +7907,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -7998,7 +7998,7 @@ { "name": "Decision scope for clinical reportable units.", "polymorphic_discriminator": "subject_template", - "category": "BSJ", + "category": "subject", "type": "generic", "subtype": "report-subject", "version": "1.0", @@ -8009,10 +8009,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -8097,7 +8097,7 @@ { "name": "Workset grouping workflow steps and objects created during an accessioning transaction.", "polymorphic_discriminator": "subject_template", - "category": "BSJ", + "category": "subject", "type": "workset", "subtype": "accession", "version": "1.0", @@ -8108,10 +8108,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -8226,7 +8226,7 @@ { "name": "Workset grouping workflow steps and objects created during an extraction batch.", "polymorphic_discriminator": "subject_template", - "category": "BSJ", + "category": "subject", "type": "workset", "subtype": "extraction", "version": "1.0", @@ -8237,10 +8237,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -8355,7 +8355,7 @@ { "name": "Workset grouping workflow steps and objects created during a sequencing run.", "polymorphic_discriminator": "subject_template", - "category": "BSJ", + "category": "subject", "type": "workset", "subtype": "sequencing", "version": "1.0", @@ -8366,10 +8366,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -8484,7 +8484,7 @@ { "name": "Workset for carrier-screen 3.9", "polymorphic_discriminator": "workflow_template", - "category": "BWF", + "category": "workflow", "type": "assay-workset", "subtype": "carrier-screen", "version": "3.9", @@ -8495,11 +8495,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "move_among_ay_top_queues": "BAC/move-queues/move-among-ay-top-queues/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "move_among_ay_top_queues": "action/move-queues/move-among-ay-top-queues/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -8519,7 +8519,7 @@ "relationship_type": "contains", "child_templates": [ { - "template_code": "BWS/queue/plasma-isolation-queue-available/1.0", + "template_code": "workflow_step/queue/plasma-isolation-queue-available/1.0", "json_addl": { "properties": { "comments": "", @@ -8528,7 +8528,7 @@ } }, { - "template_code": "BWS/queue/plasma-isolation-queue-removed/1.0", + "template_code": "workflow_step/queue/plasma-isolation-queue-removed/1.0", "json_addl": { "properties": { "comments": "", @@ -8537,7 +8537,7 @@ } }, { - "template_code": "BWS/queue/plasma-isolation-queue-exception/1.0", + "template_code": "workflow_step/queue/plasma-isolation-queue-exception/1.0", "json_addl": { "properties": { "comments": "", @@ -8565,7 +8565,7 @@ { "name": "Workset for hla-typing 1.2", "polymorphic_discriminator": "workflow_template", - "category": "BWF", + "category": "workflow", "type": "assay-workset", "subtype": "hla-typing", "version": "1.2", @@ -8576,11 +8576,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "move_among_ay_top_queues": "BAC/move-queues/move-among-ay-top-queues/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "move_among_ay_top_queues": "action/move-queues/move-among-ay-top-queues/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -8618,7 +8618,7 @@ { "name": "Carrier Screen 3.9", "polymorphic_discriminator": "workflow_template", - "category": "BWF", + "category": "workflow", "type": "assay", "subtype": "carrier-screen", "version": "3.9", @@ -8629,10 +8629,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "assay_type": "assay", "cogs": { @@ -8653,7 +8653,7 @@ "relationship_type": "contains", "child_templates": [ { - "template_code": "BWS/queue/extraction-batch-eligible/1.0", + "template_code": "workflow_step/queue/extraction-batch-eligible/1.0", "json_addl": { "description": "Extraction Batch Eligible", "properties": { @@ -8666,7 +8666,7 @@ "name_pattern": "Extraction Batch Eligible" }, { - "template_code": "BWS/queue/blood-to-gdna-extraction-eligible/1.0", + "template_code": "workflow_step/queue/blood-to-gdna-extraction-eligible/1.0", "json_addl": { "description": "Blood To gDNA Extraction Eligible", "properties": { @@ -8679,7 +8679,7 @@ "name_pattern": "Blood To gDNA Extraction Eligible" }, { - "template_code": "BWS/queue/buccal-to-gdna-extraction-eligible/1.0", + "template_code": "workflow_step/queue/buccal-to-gdna-extraction-eligible/1.0", "json_addl": { "description": "Buccal To gDNA Extraction Eligible", "properties": { @@ -8692,7 +8692,7 @@ "name_pattern": "Buccal To gDNA Extraction Eligible" }, { - "template_code": "BWS/queue/input-gdna-normalization-eligible/1.0", + "template_code": "workflow_step/queue/input-gdna-normalization-eligible/1.0", "json_addl": { "description": "Input gDNA Normalization Eligible", "properties": { @@ -8705,7 +8705,7 @@ "name_pattern": "Input gDNA Normalization Eligible" }, { - "template_code": "BWS/queue/illumina-novaseq-libprep-eligible/1.0", + "template_code": "workflow_step/queue/illumina-novaseq-libprep-eligible/1.0", "json_addl": { "description": "Illumina Novaseq LibPrep Eligible", "properties": { @@ -8718,7 +8718,7 @@ "name_pattern": "Illumina Novaseq LibPrep Eligible" }, { - "template_code": "BWS/queue/ont-libprep-eligible/1.0", + "template_code": "workflow_step/queue/ont-libprep-eligible/1.0", "json_addl": { "description": "ONT LibPrep Eligible", "properties": { @@ -8749,7 +8749,7 @@ { "name": "HLA Typing v1.2", "polymorphic_discriminator": "workflow_template", - "category": "BWF", + "category": "workflow", "type": "assay", "subtype": "hla-typing", "version": "1.2", @@ -8760,10 +8760,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "assay_type": "assay", "cogs": { @@ -8784,7 +8784,7 @@ "relationship_type": "contains", "child_templates": [ { - "template_code": "BWS/queue/extraction-batch-eligible/1.0", + "template_code": "workflow_step/queue/extraction-batch-eligible/1.0", "json_addl": { "description": "Extraction Batch Eligible", "properties": { @@ -8797,7 +8797,7 @@ "name_pattern": "Extraction Batch Eligible" }, { - "template_code": "BWS/queue/blood-to-gdna-extraction-eligible/1.0", + "template_code": "workflow_step/queue/blood-to-gdna-extraction-eligible/1.0", "json_addl": { "description": "Blood To gDNA Extraction Eligible", "properties": { @@ -8810,7 +8810,7 @@ "name_pattern": "Blood To gDNA Extraction Eligible" }, { - "template_code": "BWS/queue/buccal-to-gdna-extraction-eligible/1.0", + "template_code": "workflow_step/queue/buccal-to-gdna-extraction-eligible/1.0", "json_addl": { "description": "Buccal To gDNA Extraction Eligible", "properties": { @@ -8823,7 +8823,7 @@ "name_pattern": "Buccal To gDNA Extraction Eligible" }, { - "template_code": "BWS/queue/input-gdna-normalization-eligible/1.0", + "template_code": "workflow_step/queue/input-gdna-normalization-eligible/1.0", "json_addl": { "description": "Input gDNA Normalization Eligible", "properties": { @@ -8836,7 +8836,7 @@ "name_pattern": "Input gDNA Normalization Eligible" }, { - "template_code": "BWS/queue/illumina-novaseq-libprep-eligible/1.0", + "template_code": "workflow_step/queue/illumina-novaseq-libprep-eligible/1.0", "json_addl": { "description": "Illumina Novaseq LibPrep Eligible", "properties": { @@ -8849,7 +8849,7 @@ "name_pattern": "Illumina Novaseq LibPrep Eligible" }, { - "template_code": "BWS/queue/ont-libprep-eligible/1.0", + "template_code": "workflow_step/queue/ont-libprep-eligible/1.0", "json_addl": { "description": "ONT LibPrep Eligible", "properties": { @@ -8880,7 +8880,7 @@ { "name": "Destroy Specimen Containers", "polymorphic_discriminator": "workflow_template", - "category": "BWF", + "category": "workflow", "type": "destroy", "subtype": "specimen-containers", "version": "1.0", @@ -8891,11 +8891,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "destroy_specimen_containers": "BAC/workflow/destroy_specimen_containers/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "destroy_specimen_containers": "action/workflow/destroy_specimen_containers/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -8922,7 +8922,7 @@ { "name": "DNA extraction from blood-whole in tube-strek-10ml tubes to a fixed-plate-96 with gdna in each well.", "polymorphic_discriminator": "workflow_template", - "category": "BWF", + "category": "workflow", "type": "extraction", "subtype": "blood-whole-to-gdna", "version": "1.0", @@ -8933,10 +8933,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -8968,7 +8968,7 @@ { "name": "DNA extraction from blood-whole in tube-strek-10ml tubes to a fixed-plate-96 with gdna in each well.", "polymorphic_discriminator": "workflow_template", - "category": "BWF", + "category": "workflow", "type": "extraction", "subtype": "blood-whole-to-plasma-to-cfdna", "version": "1.0", @@ -8979,10 +8979,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9014,7 +9014,7 @@ { "name": "DNA QUANTIFIED", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "analysis", "subtype": "gdna-quantified", "version": "1.0", @@ -9025,10 +9025,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9044,44 +9044,44 @@ "expected_inputs": [ { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } } ], "expected_outputs": [ { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } } ], @@ -9107,7 +9107,7 @@ { "name": "Plasma QUANT", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "analysis", "subtype": "plasma-quantification", "version": "1.0", @@ -9118,11 +9118,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "download_quant_data": "BAC/workflow_step_analysis/download_quant_data/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "download_quant_data": "action/workflow_step_analysis/download_quant_data/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9159,7 +9159,7 @@ { "name": "Specimens Destroyed", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "destroy-steps", "subtype": "specimen-containers-destroyed", "version": "1.0", @@ -9170,11 +9170,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "complete_specimen_destroy": "BAC/workflow_step_destroy/complete_specimen_destroy/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "complete_specimen_destroy": "action/workflow_step_destroy/complete_specimen_destroy/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9211,7 +9211,7 @@ { "name": "Destroyed Containers Exceptions", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "exception", "subtype": "specimen-containers-destroyed", "version": "1.0", @@ -9222,10 +9222,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9262,7 +9262,7 @@ { "name": "cfDNA Extraction Plate ", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "extraction-steps", "subtype": "cfdna-extracted", "version": "1.0", @@ -9273,11 +9273,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "cfdna_post_extraction_quant": "BAC/workflow_step_extraction/cfdna_post_extraction_quant/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "cfdna_post_extraction_quant": "action/workflow_step_extraction/cfdna_post_extraction_quant/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9294,22 +9294,22 @@ [ { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } } ] @@ -9321,7 +9321,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/cfdna/*" + "holds_content": "material/sample/cfdna/*" } } ] @@ -9332,7 +9332,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/cfdna/*" + "holds_content": "material/sample/cfdna/*" } } ] @@ -9343,7 +9343,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/cfdna/*" + "holds_content": "material/sample/cfdna/*" } } ] @@ -9354,7 +9354,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/cfdna/*" + "holds_content": "material/sample/cfdna/*" } } ] @@ -9384,7 +9384,7 @@ { "name": "Extracting gdna from blood-whole in tubes to wells in a fixed-plate-96.", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "extraction-steps", "subtype": "gdna-extraction", "version": "1.0", @@ -9395,10 +9395,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9414,22 +9414,22 @@ "expected_inputs": [ { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } } ], @@ -9440,7 +9440,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/gdna/*" + "holds_content": "material/sample/gdna/*" } } ] @@ -9451,7 +9451,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/gdna/*" + "holds_content": "material/sample/gdna/*" } } ] @@ -9462,7 +9462,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/gdna/*" + "holds_content": "material/sample/gdna/*" } } ] @@ -9473,7 +9473,7 @@ "child_of_lineages": [ { "container/well/fixed-plate-well/*/": { - "holds_content": "content/sample/gdna/*" + "holds_content": "material/sample/gdna/*" } } ] @@ -9503,7 +9503,7 @@ { "name": "Loading blood-whole in tube-strek-10ml tubes.", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "extraction-steps", "subtype": "loading", "version": "1.0", @@ -9514,10 +9514,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9533,22 +9533,22 @@ "expected_inputs": [ { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } } ], @@ -9575,7 +9575,7 @@ { "name": "plasma isolation", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "extraction-steps", "subtype": "plasma-isolated", "version": "1.0", @@ -9586,10 +9586,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9605,44 +9605,44 @@ "expected_inputs": [ { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } }, { "container/tube/tube-strek-10ml/*/": { - "holds_content": "content/specimen/blood-whole/*" + "holds_content": "material/specimen/blood-whole/*" } } ], "expected_outputs": [ { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } }, { "container/tube/tube-generic-10ml/*/": { - "holds_content": "content/sample/blood-plasma/*" + "holds_content": "material/sample/blood-plasma/*" } } ], @@ -9668,7 +9668,7 @@ { "name": "Filled Extraction Plate", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "plate-operations", "subtype": "extraction-plate-filled", "version": "1.0", @@ -9679,15 +9679,15 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "cfdna_quant": "BAC/workflow_step_plate_operations/cfdna_quant/1.0", - "create_pool_from_containers": "BAC/pool/create_pool_from_containers__extraction_plate_filled/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_child_object": "BAC/object/set-child-object/1.0", - "set_object_status": "BAC/core/set_object_status/1.0", - "stamp_copy_plate": "BAC/workflow_step_plate_operations/stamp_copy_plate/1.0", - "stamp_plates_into_plate": "BAC/plate/stamp_plates_into_plate__extraction_plate_filled/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "cfdna_quant": "action/workflow_step_plate_operations/cfdna_quant/1.0", + "create_pool_from_containers": "action/pool/create_pool_from_containers__extraction_plate_filled/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_child_object": "action/object/set-child-object/1.0", + "set_object_status": "action/core/set_object_status/1.0", + "stamp_copy_plate": "action/workflow_step_plate_operations/stamp_copy_plate/1.0", + "stamp_plates_into_plate": "action/plate/stamp_plates_into_plate__extraction_plate_filled/1.0" }, "cogs": { "allocation_type": "", @@ -9731,7 +9731,7 @@ { "name": "Heatseal Plate", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "plate-operations", "subtype": "heat-seal", "version": "1.0", @@ -9742,10 +9742,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9790,7 +9790,7 @@ { "name": "Existing plate, had 1+ plates merged into it.", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "plate-operations", "subtype": "plate-added-to", "version": "1.0", @@ -9801,12 +9801,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "cfdna_quant": "BAC/workflow_step_plate_operations/cfdna_quant/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0", - "stamp_copy_plate": "BAC/workflow_step_plate_operations/stamp_copy_plate/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "cfdna_quant": "action/workflow_step_plate_operations/cfdna_quant/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0", + "stamp_copy_plate": "action/workflow_step_plate_operations/stamp_copy_plate/1.0" }, "cogs": { "allocation_type": "", @@ -9843,7 +9843,7 @@ { "name": "Stamp Copy Plate", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "plate-operations", "subtype": "stamp-copy-plate", "version": "1.0", @@ -9854,12 +9854,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "cfdna_quant": "BAC/workflow_step_plate_operations/cfdna_quant/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0", - "stamp_copy_plate": "BAC/workflow_step_plate_operations/stamp_copy_plate/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "cfdna_quant": "action/workflow_step_plate_operations/cfdna_quant/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0", + "stamp_copy_plate": "action/workflow_step_plate_operations/stamp_copy_plate/1.0" }, "cogs": { "allocation_type": "", @@ -9896,7 +9896,7 @@ { "name": "pool", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "pool", "subtype": "generic", "version": "1.0", @@ -9907,10 +9907,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9947,7 +9947,7 @@ { "name": "OVERRIDEME", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "all-purpose", "version": "1.0", @@ -9958,10 +9958,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -9998,7 +9998,7 @@ { "name": "Blood To gDNA Extraction Eligible", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "blood-to-gdna-extraction-eligible", "version": "1.0", @@ -10009,15 +10009,15 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_gdna_to_gdna_normalization_queue": "BAC/workflow_step_queue/add-gdna-to-gdna-normalization-queue/1.0", - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "existing_plate_fill_auto": "BAC/workflow_step_queue/existing-plate-fill-auto/1.0", - "existing_plate_fill_directed": "BAC/workflow_step_queue/existing-plate-fill-directed/1.0", - "plate_create_fill_auto": "BAC/workflow_step_queue/plate-create-fill-auto/1.0", - "plate_create_fill_directed": "BAC/workflow_step_queue/plate-create-fill-directed/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_gdna_to_gdna_normalization_queue": "action/workflow_step_queue/add-gdna-to-gdna-normalization-queue/1.0", + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "existing_plate_fill_auto": "action/workflow_step_queue/existing-plate-fill-auto/1.0", + "existing_plate_fill_directed": "action/workflow_step_queue/existing-plate-fill-directed/1.0", + "plate_create_fill_auto": "action/workflow_step_queue/plate-create-fill-auto/1.0", + "plate_create_fill_directed": "action/workflow_step_queue/plate-create-fill-directed/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10054,7 +10054,7 @@ { "name": "Buccal To gDNA Extraction Eligible", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "buccal-to-gdna-extraction-eligible", "version": "1.0", @@ -10065,15 +10065,15 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_gdna_to_gdna_normalization_queue": "BAC/workflow_step_queue/add-gdna-to-gdna-normalization-queue/1.0", - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "existing_plate_fill_auto": "BAC/workflow_step_queue/existing-plate-fill-auto/1.0", - "existing_plate_fill_directed": "BAC/workflow_step_queue/existing-plate-fill-directed/1.0", - "plate_create_fill_auto": "BAC/workflow_step_queue/plate-create-fill-auto/1.0", - "plate_create_fill_directed": "BAC/workflow_step_queue/plate-create-fill-directed/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_gdna_to_gdna_normalization_queue": "action/workflow_step_queue/add-gdna-to-gdna-normalization-queue/1.0", + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "existing_plate_fill_auto": "action/workflow_step_queue/existing-plate-fill-auto/1.0", + "existing_plate_fill_directed": "action/workflow_step_queue/existing-plate-fill-directed/1.0", + "plate_create_fill_auto": "action/workflow_step_queue/plate-create-fill-auto/1.0", + "plate_create_fill_directed": "action/workflow_step_queue/plate-create-fill-directed/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10110,7 +10110,7 @@ { "name": "Extraction Batch Eligible", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "extraction-batch-eligible", "version": "1.0", @@ -10121,12 +10121,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "add_to_blood_gdna_extraction_queue": "BAC/workflow_step_queue/add-to-blood-gdna-extraction-queue/1.0", - "add_to_buccal_gdna_extraction_queue": "BAC/workflow_step_queue/add-to-buccal-gdna-extraction-queue/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "add_to_blood_gdna_extraction_queue": "action/workflow_step_queue/add-to-blood-gdna-extraction-queue/1.0", + "add_to_buccal_gdna_extraction_queue": "action/workflow_step_queue/add-to-buccal-gdna-extraction-queue/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10163,7 +10163,7 @@ { "name": "Plasma To Plates For Extraction", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "extraction-queue-available", "version": "1.0", @@ -10174,12 +10174,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "fill_plate_directed": "BAC/workflow_step_queue/fill_plate_directed/1.0", - "fill_plate_undirected": "BAC/workflow_step_queue/fill_plate_undirected/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "fill_plate_directed": "action/workflow_step_queue/fill_plate_directed/1.0", + "fill_plate_undirected": "action/workflow_step_queue/fill_plate_undirected/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10216,7 +10216,7 @@ { "name": "Illumina Novaseq LibPrep Eligible", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "illumina-novaseq-libprep-eligible", "version": "1.0", @@ -10227,14 +10227,14 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "existing_plate_fill_auto": "BAC/workflow_step_queue/existing-plate-fill-auto/1.0", - "existing_plate_fill_directed": "BAC/workflow_step_queue/existing-plate-fill-directed/1.0", - "plate_create_fill_auto": "BAC/workflow_step_queue/plate-create-fill-auto/1.0", - "plate_create_fill_directed": "BAC/workflow_step_queue/plate-create-fill-directed/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "existing_plate_fill_auto": "action/workflow_step_queue/existing-plate-fill-auto/1.0", + "existing_plate_fill_directed": "action/workflow_step_queue/existing-plate-fill-directed/1.0", + "plate_create_fill_auto": "action/workflow_step_queue/plate-create-fill-auto/1.0", + "plate_create_fill_directed": "action/workflow_step_queue/plate-create-fill-directed/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10271,7 +10271,7 @@ { "name": "Input gDNA Normalization Eligible", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "input-gdna-normalization-eligible", "version": "1.0", @@ -10282,15 +10282,15 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "existing_plate_fill_auto": "BAC/workflow_step_queue/existing-plate-fill-auto/1.0", - "existing_plate_fill_directed": "BAC/workflow_step_queue/existing-plate-fill-directed/1.0", - "plate_create_fill_auto": "BAC/workflow_step_queue/plate-create-fill-auto/1.0", - "plate_create_fill_directed": "BAC/workflow_step_queue/plate-create-fill-directed/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "save_quant_data": "BAC/workflow_step_queue/save-quant-data/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "existing_plate_fill_auto": "action/workflow_step_queue/existing-plate-fill-auto/1.0", + "existing_plate_fill_directed": "action/workflow_step_queue/existing-plate-fill-directed/1.0", + "plate_create_fill_auto": "action/workflow_step_queue/plate-create-fill-auto/1.0", + "plate_create_fill_directed": "action/workflow_step_queue/plate-create-fill-directed/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "save_quant_data": "action/workflow_step_queue/save-quant-data/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10327,7 +10327,7 @@ { "name": "ONT LibPrep Eligible", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "ont-libprep-eligible", "version": "1.0", @@ -10338,14 +10338,14 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "existing_plate_fill_auto": "BAC/workflow_step_queue/existing-plate-fill-auto/1.0", - "existing_plate_fill_directed": "BAC/workflow_step_queue/existing-plate-fill-directed/1.0", - "plate_create_fill_auto": "BAC/workflow_step_queue/plate-create-fill-auto/1.0", - "plate_create_fill_directed": "BAC/workflow_step_queue/plate-create-fill-directed/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "existing_plate_fill_auto": "action/workflow_step_queue/existing-plate-fill-auto/1.0", + "existing_plate_fill_directed": "action/workflow_step_queue/existing-plate-fill-directed/1.0", + "plate_create_fill_auto": "action/workflow_step_queue/plate-create-fill-auto/1.0", + "plate_create_fill_directed": "action/workflow_step_queue/plate-create-fill-directed/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10382,7 +10382,7 @@ { "name": "Containers available for plasma isolation in this queue.", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "plasma-isolation-queue-available", "version": "1.0", @@ -10393,11 +10393,11 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "link_tubes_auto": "BAC/workflow_step_queue/link_tubes_auto/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "link_tubes_auto": "action/workflow_step_queue/link_tubes_auto/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10434,7 +10434,7 @@ { "name": "Plasma Isolation Exception Queue", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "plasma-isolation-queue-exception", "version": "1.0", @@ -10445,12 +10445,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "link_tubes_auto": "BAC/workflow_step_queue/link_tubes_auto/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0", - "ycreate_test_req_and_link_child_workflow_step": "BAC/workflow_step_queue/ycreate_test_req_and_link_child_workflow_step/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "link_tubes_auto": "action/workflow_step_queue/link_tubes_auto/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0", + "ycreate_test_req_and_link_child_workflow_step": "action/workflow_step_queue/ycreate_test_req_and_link_child_workflow_step/1.0" }, "cogs": { "allocation_type": "", @@ -10501,7 +10501,7 @@ { "name": "Containers Removed From Queue", "polymorphic_discriminator": "workflow_step_template", - "category": "BWS", + "category": "workflow_step", "type": "queue", "subtype": "plasma-isolation-queue-removed", "version": "1.0", @@ -10512,10 +10512,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -10563,7 +10563,7 @@ { "name": "Sequencing Library Plate 96", "polymorphic_discriminator": "container_template", - "category": "BCP", + "category": "container", "type": "plate", "subtype": "sequencing-library-plate-96", "version": "1.0", @@ -10574,12 +10574,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "download_plate_map": "BAC/plate/download_plate_map/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0", - "stamp_plates_into_plate": "BAC/plate/stamp_plates_into_plate/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "download_plate_map": "action/plate/download_plate_map/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0", + "stamp_plates_into_plate": "action/plate/stamp_plates_into_plate/1.0" }, "cogs": { "allocation_type": "", @@ -12198,7 +12198,7 @@ { "name": "Sequencing Index Plate 96", "polymorphic_discriminator": "container_template", - "category": "BCP", + "category": "container", "type": "plate", "subtype": "index-plate-96", "version": "1.0", @@ -12209,12 +12209,12 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "download_plate_map": "BAC/plate/download_plate_map/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0", - "stamp_plates_into_plate": "BAC/plate/stamp_plates_into_plate/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "download_plate_map": "action/plate/download_plate_map/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0", + "stamp_plates_into_plate": "action/plate/stamp_plates_into_plate/1.0" }, "cogs": { "allocation_type": "", @@ -13833,7 +13833,7 @@ { "name": "Generic Reagent Bottle", "polymorphic_discriminator": "container_template", - "category": "BCB", + "category": "container", "type": "bottle", "subtype": "generic", "version": "1.0", @@ -13844,10 +13844,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -13881,7 +13881,7 @@ "container_role": "bottle" }, "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [], "semantic_category": "container" @@ -13890,7 +13890,7 @@ { "name": "Flowcell", "polymorphic_discriminator": "container_template", - "category": "BCF", + "category": "container", "type": "flowcell", "subtype": "generic", "version": "1.0", @@ -13901,10 +13901,10 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "print_barcode_label": "BAC/core/print_barcode_label/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "print_barcode_label": "action/core/print_barcode_label/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -13937,7 +13937,7 @@ "lab_code": "" }, "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [], "semantic_category": "container" @@ -13946,7 +13946,7 @@ { "name": "Flowcell Lane", "polymorphic_discriminator": "container_template", - "category": "BCE", + "category": "container", "type": "flowcell_lane", "subtype": "generic", "version": "1.0", @@ -13957,7 +13957,7 @@ "json_addl": { "action_groups": {}, "action_imports": { - "set_object_status": "BAC/core/set_object_status/1.0" + "set_object_status": "action/core/set_object_status/1.0" }, "cogs": { "allocation_type": "", @@ -13989,7 +13989,7 @@ }, "singleton": "0", "valid_content": [ - "content/.*" + "material/.*" ], "valid_parents": [ "container/plate/fixed-plate.*?/.*?" @@ -14000,7 +14000,7 @@ { "name": "Saliva Specimen", "polymorphic_discriminator": "content_template", - "category": "BNA", + "category": "material", "type": "specimen", "subtype": "saliva", "version": "1.0", @@ -14038,7 +14038,7 @@ { "name": "Sequencing Library Content", "polymorphic_discriminator": "content_template", - "category": "BNQ", + "category": "material", "type": "sample", "subtype": "sequencing-library", "version": "1.0", @@ -14078,7 +14078,7 @@ { "name": "Sequencing Library Pool Content", "polymorphic_discriminator": "content_template", - "category": "BNP", + "category": "material", "type": "pool", "subtype": "sequencing-library", "version": "1.0", @@ -14117,7 +14117,7 @@ { "name": "Sequencing Index Reagent Content", "polymorphic_discriminator": "content_template", - "category": "BNX", + "category": "material", "type": "reagent", "subtype": "sequencing-index", "version": "1.0", @@ -14166,7 +14166,7 @@ { "name": "gDNA Quantification Data", "polymorphic_discriminator": "data_template", - "category": "BDQ", + "category": "data", "type": "quantification", "subtype": "gdna", "version": "1.0", @@ -14177,9 +14177,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Generic data record", "expected_inputs": [], @@ -14207,7 +14207,7 @@ { "name": "Extraction Run Evidence", "polymorphic_discriminator": "data_template", - "category": "BDX", + "category": "data", "type": "operation", "subtype": "extraction", "version": "1.0", @@ -14218,9 +14218,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Generic data record", "expected_inputs": [], @@ -14245,7 +14245,7 @@ { "name": "Extraction QC Run Evidence", "polymorphic_discriminator": "data_template", - "category": "BDY", + "category": "data", "type": "operation", "subtype": "extraction-qc", "version": "1.0", @@ -14256,9 +14256,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Generic data record", "expected_inputs": [], @@ -14281,7 +14281,7 @@ { "name": "Library Prep Run Evidence", "polymorphic_discriminator": "data_template", - "category": "BDP", + "category": "data", "type": "operation", "subtype": "library-prep", "version": "1.0", @@ -14292,9 +14292,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Generic data record", "expected_inputs": [], @@ -14317,7 +14317,7 @@ { "name": "Pooling Run Evidence", "polymorphic_discriminator": "data_template", - "category": "BDN", + "category": "data", "type": "operation", "subtype": "pooling", "version": "1.0", @@ -14328,9 +14328,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Generic data record", "expected_inputs": [], @@ -14353,7 +14353,7 @@ { "name": "Library Index Assignment Data", "polymorphic_discriminator": "data_template", - "category": "BDA", + "category": "data", "type": "library-index-assignment", "subtype": "sequencing-library", "version": "1.0", @@ -14364,9 +14364,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Generic data record", "expected_inputs": [], @@ -14391,7 +14391,7 @@ { "name": "Transfer Execution Data", "polymorphic_discriminator": "data_template", - "category": "BDT", + "category": "data", "type": "execution", "subtype": "transfer", "version": "1.0", @@ -14402,9 +14402,9 @@ "json_addl": { "action_groups": {}, "action_imports": { - "add_relationships": "BAC/core/add-relationships/1.0", - "create_subject_and_anchor": "BAC/core/create-subject-and-anchor/1.0", - "set_object_status": "BAC/core/set_object_status/1.0" + "add_relationships": "action/core/add-relationships/1.0", + "create_subject_and_anchor": "action/core/create-subject-and-anchor/1.0", + "set_object_status": "action/core/set_object_status/1.0" }, "description": "Generic data record", "expected_inputs": [], @@ -14427,9 +14427,9 @@ { "name": "Bloom Anomaly Record", "polymorphic_discriminator": "generic_template", - "category": "BAN", - "type": "ops", - "subtype": "anomaly-record", + "category": "ops", + "type": "anomaly-record", + "subtype": "generic", "version": "1.0", "instance_prefix": "BAN", "instance_polymorphic_identity": "generic_instance", diff --git a/docs/apis.md b/docs/apis.md index f283ea6..1f942d3 100644 --- a/docs/apis.md +++ b/docs/apis.md @@ -287,7 +287,7 @@ curl -k https://localhost:8912/api/v1/external/specimens \ -H "Idempotency-Key: atlas-specimen-001" \ -H "Content-Type: application/json" \ -d '{ - "specimen_template_code": "content/specimen/blood-whole/1.0", + "specimen_template_code": "material/specimen/blood-whole/1.0", "specimen_name": "specimen-demo", "status": "active", "container_template_code": "container/tube/tube-generic-10ml/1.0", diff --git a/docs/lab_actions.md b/docs/lab_actions.md index 3b5ec61..f3d0031 100644 --- a/docs/lab_actions.md +++ b/docs/lab_actions.md @@ -19,16 +19,16 @@ The current flow uses the existing Bloom template pack plus one new generic run- | Purpose | Semantic template path | Instance prefix | Notes | |---|---|---:|---| -| Generic run/set object | `set/run-set/generic/1.01` | `BGS` | New template. Seeded as `category=BGS`, `type=run-set`, `subtype=generic`, `version=1.01`. Holds named sets, run metadata, members, external members, status, and arbitrary metadata. | +| Generic run/set object | `set/run-set/generic/1.01` | `BGS` | New template. Seeded as `category=set`, `type=run-set`, `subtype=generic`, `version=1.01`. Holds named sets, run metadata, members, external members, status, and arbitrary metadata. | | Extraction plate | `container/plate/fixed-plate-96/1.0` | `BCP` | Existing recursive 96-well plate template. Creates linked well containers. | | Sequencing-library plate | `container/plate/sequencing-library-plate-96/1.0` | `BCP` | Existing recursive 96-well plate template. Creates linked well containers. | | Plate well | `container/well/fixed-plate-well/1.0` | `BCW` | Created by recursive plate templates. | | Pool tube | `container/tube/tube-generic-10ml/1.0` | `BCT` | Existing generic tube template. More specific pool-tube templates can be added later if needed. | -| Incoming specimen contents | `content/specimen/{blood-whole,buccal-swab,saliva}/1.0` | `BNB`, `BNS`, `BNA` | Existing whole-blood, buccal-swab, and saliva specimen templates. Incoming tubes must already hold one content object unless the caller creates/fills the tube first. | -| Extraction well gDNA | `content/sample/gdna/1.0` | `BNG` | Created during extraction mapping. | -| Sequencing-library content | `content/sample/sequencing-library/1.0` | `BNQ` | Created during library plate mapping. | -| Sequencing-library pool content | `content/pool/sequencing-library/1.0` | `BNP` | Created when filling the pool tube. | -| Sequencing index reagent | `content/reagent/sequencing-index/1.0` | `BNX` | Optional; callers may also store an index barcode string directly. | +| Incoming specimen contents | `material/specimen/{blood-whole,buccal-swab,saliva}/1.0` | `BNB`, `BNS`, `BNA` | Existing whole-blood, buccal-swab, and saliva specimen templates. Incoming tubes must already hold one material object unless the caller creates/fills the tube first. | +| Extraction well gDNA | `material/sample/gdna/1.0` | `BNG` | Created during extraction mapping. | +| Sequencing-library content | `material/sample/sequencing-library/1.0` | `BNQ` | Created during library plate mapping. | +| Sequencing-library pool content | `material/pool/sequencing-library/1.0` | `BNP` | Created when filling the pool tube. | +| Sequencing index reagent | `material/reagent/sequencing-index/1.0` | `BNX` | Optional; callers may also store an index barcode string directly. | | gDNA quant data | `data/quantification/gdna/1.0` | `BDQ` | Optional per-well extraction output data. | No additional templates are required for the current implementation. The semantic paths above are the operator-facing shorthand used by the API and docs; the seed file stores Bloom's actual prefix-backed template identity in `category`, `type`, `subtype`, `version`, and `instance_prefix`. If production operators need separate EUID prefixes or stricter metadata for pool tubes, flowcells, reagent sets, or run kits, add those templates as a separate versioned template-pack change and update this matrix before production rollout. diff --git a/docs/old_docs/ATLAS_BLOOM_CONTRACT_TESTS.md b/docs/old_docs/ATLAS_BLOOM_CONTRACT_TESTS.md index 790482e..c77dc64 100644 --- a/docs/old_docs/ATLAS_BLOOM_CONTRACT_TESTS.md +++ b/docs/old_docs/ATLAS_BLOOM_CONTRACT_TESTS.md @@ -36,7 +36,7 @@ def test_create_container_contract(client): ```python def test_specimen_crud_contract(client, container_euid): create_payload = { - "specimen_template_code": "content/specimen/blood-whole/1.0", + "specimen_template_code": "material/specimen/blood-whole/1.0", "container_euid": container_euid, "atlas_refs": {"trf_euid": "TRF-1", "patient_id": "PAT-1", "kit_barcode": "KIT-1"}, "properties": {"source": "atlas"}, diff --git a/docs/old_docs/TAPDB_SPECIFICATION.md b/docs/old_docs/TAPDB_SPECIFICATION.md index b796a6d..4901180 100644 --- a/docs/old_docs/TAPDB_SPECIFICATION.md +++ b/docs/old_docs/TAPDB_SPECIFICATION.md @@ -998,7 +998,7 @@ Templates are identified by a **template code string** with the format: **Examples:** - `container/plate/fixed-plate-96/1.0/` - `workflow/assay/ngs-library-prep/2.1/` -- `content/sample/blood-specimen/1.0/` +- `material/sample/blood-specimen/1.0/` - `equipment/instrument/sequencer/1.0/` ### 4.2 Template JSON Structure diff --git a/docs/plans/20260520T064952Z_bloom_sequencing_run_ledger.md b/docs/plans/20260520T064952Z_bloom_sequencing_run_ledger.md index e7be661..9cb8007 100644 --- a/docs/plans/20260520T064952Z_bloom_sequencing_run_ledger.md +++ b/docs/plans/20260520T064952Z_bloom_sequencing_run_ledger.md @@ -15,7 +15,7 @@ Add a first-class Bloom sequencing run concept for the Dayhoff-deployed Bloom se | Repo state | `git status --short --branch -> ## main...origin/main` | | Instructions read | `/Users/jmajor/projects/mega_dayhoff/repos_work/bloom/AGENTS.md`, `/Users/jmajor/projects/mega_dayhoff/dayhoff/AGENTS.md`, `/Users/jmajor/.agents/AGENTS.md`, `/Users/jmajor/.agents/AGENTS.md~`, `/Users/jmajor/.codex/docs/plan-ledger-workflow.md`, `/Users/jmajor/projects/mega_dayhoff/repos_work/bloom/.augment/RULES.md` | | Existing pool/run surface | `bloom_lims/api/v1/beta_lab.py` already exposes `POST /api/v1/external/atlas/beta/pools`, `POST /api/v1/external/atlas/beta/runs`, and `GET /api/v1/external/atlas/beta/runs/{run_euid}/resolve` | -| Existing pool implementation | `bloom_lims/domain/beta_lab_stages.py::create_pool` already creates `content/pool/generic/1.0`, a pool tube, `contains` lineage, and `beta_pool_member` edges | +| Existing pool implementation | `bloom_lims/domain/beta_lab_stages.py::create_pool` already creates `material/pool/generic/1.0`, a pool tube, `contains` lineage, and `beta_pool_member` edges | | Existing run gap | `create_run` stores runs as `data/generic/generic/1.0` with `beta_kind=sequencing_run`; `BetaRunCreateRequest.platform` is `Literal["ILMN"]`; run properties do not have explicit `operator_start_datetime`, `sequencing_end_datetime`, `instrument_euid`, or run subtype | | Template gap | `config/tapdb_templates/bloom/templates.json` has `data/wetlab/library_prep_output/1.0` and equipment sequencer templates, but no `data/sequencing_run/*/1.0` templates | | Atlas boundary | Bloom `.augment/RULES.md` requires Bloom and Atlas to interact only through published APIs; this change will preserve existing external-reference lineage and not read/write Atlas DBs | @@ -29,7 +29,7 @@ Add a first-class Bloom sequencing run concept for the Dayhoff-deployed Bloom se | BSR-001 | Template pack | Add first-class sequencing run templates with subtypes for Illumina, ONT, and NovaSeq. | SUCCESS | feature_implementation | Gate 3 | orchestrator | `config/tapdb_templates/bloom/templates.json`; live seed verified `BDT/sequencing_run/{illumina,novaseq,ont}/1.0` with EUIDs `Z-TPX-50P`, `Z-TPX-52J`, `Z-TPX-51M` | Missing template-backed sequencing-run data records; runs were generic data rows. | Added and seeded three Bloom-owned sequencing run templates. | | BSR-002 | API schema | Extend beta run create/response schema with run subtype, operator start datetime, sequencing end datetime, and instrument linkage while preserving explicit validation. | SUCCESS | feature_implementation | Gate 3 | orchestrator | `bloom_lims/schemas/beta_lab.py`; public `https://bloom.dev.lsmc.life/openapi.json` exposes `platform` enum `ILMN,ONT`, `run_subtype`, `operator_start_datetime`, `sequencing_end_datetime`, and `instrument_euid` | Existing beta run schema only modeled ILMN flowcell runs. | Schema accepts Illumina, ONT, NovaSeq subtype contract and rejects mismatched platform/subtype and naive/out-of-order datetimes. | | BSR-003 | Domain persistence | Store sequencing runs using the subtype-specific sequencing run template, link optional instrument via existing `beta_used_instrument`, and preserve pool/library/assignment lineage. | SUCCESS | feature_implementation | Gate 3 | orchestrator | `bloom_lims/domain/beta_lab.py`, `bloom_lims/domain/beta_lab_store.py`, `bloom_lims/domain/beta_lab_stages.py`; local DB-backed `tests/test_queue_flow.py::test_beta_queue_flow_end_to_end` verifies run instance `type=sequencing_run`, `subtype=novaseq`, persisted timing fields, and `beta_used_instrument` lineage | Persistence always created `data/generic/generic/1.0` rows for sequencing runs. | Run creation now chooses `data/sequencing_run/{illumina,ont,novaseq}/1.0` and still keeps pool, library assignment, artifact, and Atlas fulfillment lineage. | -| BSR-004 | Pool assessment | Verify whether pooled sequencing library already exists and avoid adding a duplicate concept if current `content/pool/generic/1.0` plus pool tube/member lineage satisfies the request. | SUCCESS | feature_implementation | Gate 3 | orchestrator | Existing `create_pool` creates `content/pool/generic/1.0`, pool tube, `contains`, and `beta_pool_member` edges; `tests/test_queue_flow.py::test_beta_queue_flow_end_to_end` exercises that pool before creating the run | Pool concept was uncertain, but not absent. | No duplicate pool model added; existing pool content plus container/member lineage satisfies the request. | +| BSR-004 | Pool assessment | Verify whether pooled sequencing library already exists and avoid adding a duplicate concept if current `material/pool/generic/1.0` plus pool tube/member lineage satisfies the request. | SUCCESS | feature_implementation | Gate 3 | orchestrator | Existing `create_pool` creates `material/pool/generic/1.0`, pool tube, `contains`, and `beta_pool_member` edges; `tests/test_queue_flow.py::test_beta_queue_flow_end_to_end` exercises that pool before creating the run | Pool concept was uncertain, but not absent. | No duplicate pool model added; existing pool content plus container/member lineage satisfies the request. | | BSR-005 | Tests | Add focused tests for sequencing run templates, schema validation, persisted run fields, instrument linkage, and existing pool lineage. | SUCCESS | contract_test | Gate 5 | orchestrator | `pytest tests/test_sequencing_run_contract.py tests/test_queue_flow.py::test_beta_queue_flow_end_to_end tests/test_run_resolver.py::test_run_resolver_returns_404_for_unknown_index -q --no-cov` -> `4 passed`; `jq empty config/tapdb_templates/bloom/templates.json`; `git diff --check` | Contract needed coverage for template loadability and first-class run persistence. | Focused local tests pass. | | BSR-006 | Live deploy | Patch AWS-deployed Bloom, seed any new Bloom-owned templates, restart only Bloom, and verify public/auth/readiness behavior. | SUCCESS | contract_test | Gate 5 | orchestrator | Live target `i-09126000eb19643b0`, repo `/home/ubuntu/.cache/dayhoff/local/lsmcok1/repos/bloom`, config `/home/ubuntu/.config/bloom-lsmcok1/bloom-config-lsmcok1.yaml`; `bloom --config ... db seed` -> `seed=ok`; old Bloom PID `238229`, new Bloom PID `264810`; `https://bloom.dev.lsmc.life/readyz` -> `ready=true`, DB `status=ok` | AWS Bloom needed the same template/schema/domain patch and live TapDB seed. | AWS Bloom patched, templates seeded, and only Bloom restarted. | diff --git a/docs/plans/20260618T000000Z_bloom_lab_action_flow_ledger.md b/docs/plans/20260618T000000Z_bloom_lab_action_flow_ledger.md index a2befa9..b9d5ade 100644 --- a/docs/plans/20260618T000000Z_bloom_lab_action_flow_ledger.md +++ b/docs/plans/20260618T000000Z_bloom_lab_action_flow_ledger.md @@ -15,7 +15,7 @@ Build a Bloom-owned lab actions layer for extraction plates, sequencing library - Existing primitives found: - recursive template create in `BloomObj.create_instances` - `container/plate/fixed-plate-96/1.0`, `container/plate/sequencing-library-plate-96/1.0`, `container/well/fixed-plate-well/1.0` - - `content/sample/gdna/1.0`, `content/sample/sequencing-library/1.0`, `content/pool/sequencing-library/1.0`, `content/reagent/sequencing-index/1.0` + - `material/sample/gdna/1.0`, `material/sample/sequencing-library/1.0`, `material/pool/sequencing-library/1.0`, `material/reagent/sequencing-index/1.0` - `data/quantification/gdna/1.0`, `data/sequencing_run/*/1.0` - Zebra Day service wrapper for print dispatch @@ -57,19 +57,19 @@ Build a Bloom-owned lab actions layer for extraction plates, sequencing library - `source ./activate dev && python -m pytest tests/test_lab_actions_api.py tests/test_bloom_prefix_taxonomy.py::test_wet_lab_templates_use_bloom_prefix_taxonomy tests/test_route_coverage_gaps_api.py::test_object_creation_plate_creates_96_linked_wells tests/test_template_instantiation_recursion.py tests/test_api_v1.py::TestObjectCreationAPI tests/test_api_v1.py::TestObjectCreationPathTraversal -q --cov-reset --cov=bloom_lims.api.v1.lab_actions --cov=bloom_lims.domain.lab_actions --cov=bloom_lims.schemas.lab_actions --cov=bloom_lims.api.v1.object_creation --cov=bloom_lims.schemas.objects --cov-report=term-missing --cov-report=json:coverage-lab-actions.json --cov-fail-under=81` -> 30 passed; total changed-surface coverage 83.81%. - Full repo test collection is not clean in the local Bloom env: `tests/test_beta_cross_repo_smoke.py` imports Ursa and requires `daylily-ephemeral-cluster`; broad non-E2E collection also stalled before progress in local subprocess handling. These are not lab-action source failures. - Template inventory from `config/tapdb_templates/bloom/templates.json` confirmed these seeded records for the lab-action flow: - - `BGS/run-set/generic/1.01` - - `BCP/plate/fixed-plate-96/1.0` - - `BCP/plate/sequencing-library-plate-96/1.0` - - `BCW/well/fixed-plate-well/1.0` - - `BCT/tube/tube-generic-10ml/1.0` - - `BNB/specimen/blood-whole/1.0` - - `BNS/specimen/buccal-swab/1.0` - - `BNA/specimen/saliva/1.0` - - `BNG/sample/gdna/1.0` - - `BNQ/sample/sequencing-library/1.0` - - `BNP/pool/sequencing-library/1.0` - - `BNX/reagent/sequencing-index/1.0` - - `BDQ/quantification/gdna/1.0` + - `set/run-set/generic/1.01` + - `container/plate/fixed-plate-96/1.0` + - `container/plate/sequencing-library-plate-96/1.0` + - `container/well/fixed-plate-well/1.0` + - `container/tube/tube-generic-10ml/1.0` + - `material/specimen/blood-whole/1.0` + - `material/specimen/buccal-swab/1.0` + - `material/specimen/saliva/1.0` + - `material/sample/gdna/1.0` + - `material/sample/sequencing-library/1.0` + - `material/pool/sequencing-library/1.0` + - `material/reagent/sequencing-index/1.0` + - `data/quantification/gdna/1.0` - Local browser proof used repo test-runtime config plus `BLOOM_OAUTH=no`; Playwright captured `/lab-actions` at `output/playwright/bloom_lab_actions/lab_actions_wizard_local.png`. - Local browser console still shows the pre-existing `/api/v1/me/preferences` 503 under test-runtime config; `/lab-actions` itself returned 200 and static assets loaded. - Docs pass added `docs/lab_actions.md` and linked it from `README.md`, `docs/apis.md`, `docs/gui.md`, and `docs/architecture.md`. diff --git a/pyproject.toml b/pyproject.toml index 0c96472..f6171e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ license = {text = "MIT"} requires-python = ">=3.12" dependencies = [ "cli-core-yo==2.1.1", - "daylily-tapdb @ git+https://github.com/Daylily-Informatics/daylily-tapdb.git@9.0.5", + "daylily-tapdb @ git+https://github.com/Daylily-Informatics/daylily-tapdb.git@9.0.6", "daylily-auth-cognito==2.1.5", "IPython>=8.18.1", "bandit[toml]>=1.8.0", diff --git a/tests/test_atlas_workflow_contract.py b/tests/test_atlas_workflow_contract.py index 3d2eb6d..070cf54 100644 --- a/tests/test_atlas_workflow_contract.py +++ b/tests/test_atlas_workflow_contract.py @@ -142,7 +142,7 @@ def _create_specimen_payload( container_euid: str | None = None, ) -> dict: payload = { - "specimen_template_code": "content/specimen/blood-whole/1.0", + "specimen_template_code": "material/specimen/blood-whole/1.0", "specimen_name": f"specimen-{_suffix()}", "status": "active", "properties": {"source": "atlas-contract-test"}, @@ -260,7 +260,7 @@ def get_container_trf_context( "/api/v1/external/specimens", headers={"Idempotency-Key": _opaque("idem", 16)}, json={ - "specimen_template_code": "content/specimen/blood-whole/1.0", + "specimen_template_code": "material/specimen/blood-whole/1.0", "specimen_name": "context-mismatch", "container_euid": container["euid"], "status": "active", @@ -315,7 +315,7 @@ def get_container_trf_context( created = _create_specimen( client, payload={ - "specimen_template_code": "content/specimen/blood-whole/1.0", + "specimen_template_code": "material/specimen/blood-whole/1.0", "specimen_name": "context-match", "container_euid": container["euid"], "status": "active", @@ -408,7 +408,7 @@ def test_unsupported_template_returns_validation_error(monkeypatch): response = client.post( "/api/v1/external/specimens", json={ - "specimen_template_code": "content/specimen/saliva/1.0", + "specimen_template_code": "material/specimen/saliva/1.0", "specimen_name": "unsupported-template", "status": "active", "atlas_refs": {"patient_id": f"PAT-{_suffix()}"}, diff --git a/tests/test_bloom_prefix_taxonomy.py b/tests/test_bloom_prefix_taxonomy.py index 12b3181..901f31d 100644 --- a/tests/test_bloom_prefix_taxonomy.py +++ b/tests/test_bloom_prefix_taxonomy.py @@ -24,9 +24,8 @@ def _template_pack() -> dict: def _templates_by_code() -> dict[str, dict]: templates: dict[str, dict] = {} for template in _template_pack()["templates"]: - semantic_category = template["json_addl"].get("semantic_category") code = ( - f"{semantic_category}/{template['type']}/" + f"{template['category']}/{template['type']}/" f"{template['subtype']}/{template['version']}" ) templates[code] = template @@ -45,14 +44,14 @@ def test_wet_lab_templates_use_bloom_prefix_taxonomy() -> None: "container/bottle/generic/1.0": "BCB", "container/flowcell/generic/1.0": "BCF", "container/flowcell_lane/generic/1.0": "BCE", - "content/specimen/blood-whole/1.0": "BNB", - "content/specimen/buccal-swab/1.0": "BNS", - "content/specimen/saliva/1.0": "BNA", - "content/sample/gdna/1.0": "BNG", - "content/sample/cfdna/1.0": "BNC", - "content/sample/sequencing-library/1.0": "BNQ", - "content/pool/sequencing-library/1.0": "BNP", - "content/reagent/sequencing-index/1.0": "BNX", + "material/specimen/blood-whole/1.0": "BNB", + "material/specimen/buccal-swab/1.0": "BNS", + "material/specimen/saliva/1.0": "BNA", + "material/sample/gdna/1.0": "BNG", + "material/sample/cfdna/1.0": "BNC", + "material/sample/sequencing-library/1.0": "BNQ", + "material/pool/sequencing-library/1.0": "BNP", + "material/reagent/sequencing-index/1.0": "BNX", "data/generic/gdna-quantification/1.0": "BDQ", "data/quantification/gdna/1.0": "BDQ", "data/operation/extraction/1.0": "BDX", @@ -67,7 +66,7 @@ def test_wet_lab_templates_use_bloom_prefix_taxonomy() -> None: for code, expected_prefix in expected_prefixes.items(): template = templates[code] - assert template["category"] == expected_prefix + assert template["category"] != expected_prefix assert template["instance_prefix"] == expected_prefix @@ -82,7 +81,7 @@ def test_anomaly_template_is_packaged_for_observability() -> None: template["subtype"], template["version"], ) - == ("BAN", "ops", "anomaly-record", "1.0") + == ("ops", "anomaly-record", "generic", "1.0") ) assert anomaly["instance_prefix"] == "BAN" @@ -171,12 +170,10 @@ def test_readme_documents_taxonomy_and_historical_object_rule() -> None: readme = (PROJECT_ROOT / "README.md").read_text(encoding="utf-8") for snippet in ( - "BC*` | Containers", - "BN*` | Contents, materials, and reagents", - "BD*` | Data and evidence objects", - "BR*` | Runs and executions", - "BG*` | Generic helpers and external-object mappings", + "Bloom models physical/material execution", + "containers, materials/content, equipment", + "recursive template creation", "Existing historical objects keep their already minted EUIDs", - "Prefixes are governance and display labels only", + "issuance and governance labels only", ): assert snippet in readme diff --git a/tests/test_cli_db_command_paths.py b/tests/test_cli_db_command_paths.py index ff04b28..d9aba92 100644 --- a/tests/test_cli_db_command_paths.py +++ b/tests/test_cli_db_command_paths.py @@ -489,23 +489,23 @@ def test_seed_templates_split_core_and_client_ownership( lambda _dirs, strict: ( [ { - "category": "SYS", - "type": "system", - "subtype": "config", + "category": "actor", + "type": "user", + "subtype": "system", "version": "1.0", "instance_prefix": "SYS", "_source_file": str(core_template_path), }, { - "category": "XRF", - "type": "reference", - "subtype": "external_object", + "category": "reference", + "type": "external_identifier", + "subtype": "tapdb_object", "version": "1.0", "instance_prefix": "XRF", "_source_file": str(core_template_path), }, { - "category": "BAC", + "category": "action", "type": "beta_lab", "subtype": "claim_material_in_queue", "version": "1.0", @@ -615,31 +615,34 @@ def dispose(self): def test_retire_obsolete_template_variants_only_deletes_stale_prefixes() -> None: templates = [ { - "category": "BRM", + "category": "data", "type": "sequencing_run", "subtype": "illumina", "version": "1.0", + "instance_prefix": "BRM", "json_addl": {"semantic_category": "data"}, }, { - "category": "BRN", + "category": "data", "type": "sequencing_run", "subtype": "ont", "version": "1.0", + "instance_prefix": "BRN", "json_addl": {"semantic_category": "data"}, }, { - "category": "BCT", + "category": "container", "type": "tube", "subtype": "tube-generic-10ml", "version": "1.0", + "instance_prefix": "BCN", "json_addl": {"semantic_category": "container"}, }, ] current = SimpleNamespace( domain_code="Z", type="sequencing_run", - category="BRM", + category="data", subtype="illumina", version="1.0", is_deleted=False, @@ -728,14 +731,16 @@ def flush(self): domain_code="Z", ) - assert retired == 2 + assert retired == 4 assert current.is_deleted is False assert stale_prefix.is_deleted is True assert stale_prefix.bstatus == "retired" - assert stale_subtype.is_deleted is False + assert stale_subtype.is_deleted is True + assert stale_subtype.bstatus == "retired" assert stale_container_prefix.is_deleted is True assert stale_container_prefix.bstatus == "retired" - assert non_data.is_deleted is False + assert non_data.is_deleted is True + assert non_data.bstatus == "retired" assert fake_session.flushed == 1 diff --git a/tests/test_core_validation.py b/tests/test_core_validation.py index 0301f3d..388e728 100644 --- a/tests/test_core_validation.py +++ b/tests/test_core_validation.py @@ -224,7 +224,7 @@ def test_valid_reference_pattern(self): # Valid patterns assert validator._is_valid_reference("action/generic/test/1.0") assert validator._is_valid_reference("workflow/dna_extraction/v1/1.0") - assert validator._is_valid_reference("content/sample/blood/*/") + assert validator._is_valid_reference("material/sample/blood/*/") # Invalid patterns assert not validator._is_valid_reference("invalid") diff --git a/tests/test_packaging_metadata.py b/tests/test_packaging_metadata.py index 0aa2123..63c5e79 100644 --- a/tests/test_packaging_metadata.py +++ b/tests/test_packaging_metadata.py @@ -15,7 +15,7 @@ def test_project_dependencies_pin_release_train_versions() -> None: assert "cli-core-yo==2.1.1" in dependencies assert "daylily-auth-cognito==2.1.5" in dependencies - assert "daylily-tapdb @ git+https://github.com/Daylily-Informatics/daylily-tapdb.git@9.0.4" in dependencies + assert "daylily-tapdb @ git+https://github.com/Daylily-Informatics/daylily-tapdb.git@9.0.6" in dependencies assert "IPython>=8.18.1" in dependencies assert "psycopg2-binary==2.9.12" in dependencies assert not any(str(dep).startswith("zebra-day") for dep in dependencies) diff --git a/tests/test_sequencing_run_contract.py b/tests/test_sequencing_run_contract.py index cb6d1c2..d689d84 100644 --- a/tests/test_sequencing_run_contract.py +++ b/tests/test_sequencing_run_contract.py @@ -63,7 +63,7 @@ def test_sequencing_run_templates_are_shipped_for_tapdb_seed(): for template_key, (platform, instance_prefix) in expected.items(): template = templates[template_key] props = template["json_addl"]["properties"] - assert template["category"] == instance_prefix + assert template["category"] == "data" assert template["instance_prefix"] == instance_prefix assert template["json_addl"]["semantic_category"] == "data" assert props["beta_kind"] == "sequencing_run" diff --git a/uv.lock b/uv.lock index 335c05a..74ffb0e 100644 --- a/uv.lock +++ b/uv.lock @@ -200,7 +200,7 @@ requires-dist = [ { name = "cli-core-yo", specifier = "==2.1.1" }, { name = "daylily-auth-cognito", specifier = "==2.1.5" }, { name = "daylily-carrier-tracking", specifier = "==0.5.1" }, - { name = "daylily-tapdb", git = "https://github.com/Daylily-Informatics/daylily-tapdb.git?rev=9.0.5" }, + { name = "daylily-tapdb", git = "https://github.com/Daylily-Informatics/daylily-tapdb.git?rev=9.0.6" }, { name = "djlint" }, { name = "fastapi", specifier = ">=0.110.0" }, { name = "httpx", specifier = ">=0.27.2" }, @@ -704,8 +704,8 @@ wheels = [ [[package]] name = "daylily-tapdb" -version = "9.0.5" -source = { git = "https://github.com/Daylily-Informatics/daylily-tapdb.git?rev=9.0.5#0230d3c9bc8e3ef49e948ce3f8a7169b8e90a0be" } +version = "9.0.6" +source = { git = "https://github.com/Daylily-Informatics/daylily-tapdb.git?rev=9.0.6#ea195137638d1cc49b4499c9bf778c57b6d70d9b" } dependencies = [ { name = "cli-core-yo" }, { name = "jsonschema" }, From af6b81bbf69e8096f538018e5771220451a01abb Mon Sep 17 00:00:00 2001 From: John Major Date: Fri, 19 Jun 2026 09:50:38 -0700 Subject: [PATCH 44/47] Add Bloom container action API and GUI --- bloom_lims/api/v1/__init__.py | 3 + bloom_lims/api/v1/container_actions.py | 248 ++++++++++++++++++++++++ bloom_lims/gui/routes/modern.py | 13 ++ templates/modern/base.html | 3 + templates/modern/container_actions.html | 155 +++++++++++++++ tests/test_lab_actions_api.py | 108 +++++++++++ 6 files changed, 530 insertions(+) create mode 100644 bloom_lims/api/v1/container_actions.py create mode 100644 templates/modern/container_actions.html diff --git a/bloom_lims/api/v1/__init__.py b/bloom_lims/api/v1/__init__.py index 0c03ab7..7fd8888 100644 --- a/bloom_lims/api/v1/__init__.py +++ b/bloom_lims/api/v1/__init__.py @@ -21,6 +21,7 @@ from .batch import router as batch_router from .beta_lab import router as beta_lab_router from .containers import router as containers_router +from .container_actions import router as container_actions_router from .content import router as content_router from .equipment import router as equipment_router from .execution_queue import router as execution_queue_router @@ -45,6 +46,7 @@ router.include_router(auth_router) router.include_router(preferences_router) router.include_router(containers_router) +router.include_router(container_actions_router) router.include_router(content_router) router.include_router(equipment_router) router.include_router(execution_queue_router) @@ -77,6 +79,7 @@ async def api_v1_info(): "objects": "/api/v1/objects", "auth": "/api/v1/auth", "containers": "/api/v1/containers", + "container_actions": "/api/v1/container-actions", "content": "/api/v1/content", "equipment": "/api/v1/equipment", "execution": "/api/v1/execution", diff --git a/bloom_lims/api/v1/container_actions.py b/bloom_lims/api/v1/container_actions.py new file mode 100644 index 0000000..440324f --- /dev/null +++ b/bloom_lims/api/v1/container_actions.py @@ -0,0 +1,248 @@ +"""Generic Bloom container-action validation facade.""" + +from __future__ import annotations + +import csv +from io import StringIO +from typing import Any, Literal + +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel, Field, ValidationError +from sqlalchemy import select + +from bloom_lims.tapdb_adapter import BLOOMdb3, generic_instance +from bloom_lims.domain.lab_actions import LabActionsService +from bloom_lims.schemas.lab_actions import ( + ExtractionPlateRequest, + ExtractionQcPlateRequest, + LabSetRequest, + PlateWellDataRequest, + PrintEuidRequest, + SeqLibraryPlateRequest, + SeqLibraryPoolRequest, + SeqRunSetRequest, +) + +from .dependencies import APIUser, require_write + +router = APIRouter(prefix="/container-actions", tags=["Container Actions"]) + + +class ContainerActionScanRequest(BaseModel): + rows: str | None = None + records: list[dict[str, Any]] = Field(default_factory=list) + default_target: str | None = None + child_name: str | None = None + child_description: str | None = None + child_content_type: str | None = None + + +ContainerActionOperation = Literal[ + "extraction_plate", + "extraction_qc_plate", + "plate_well_data", + "seq_library_plate", + "seq_library_pool", + "seq_run_set", + "lab_set", + "print_euids", +] + + +class ContainerActionExecuteRequest(ContainerActionScanRequest): + operation_type: ContainerActionOperation | None = None + operation_payload: dict[str, Any] | None = None + + +def _service_for_user(user: APIUser) -> LabActionsService: + return LabActionsService( + BLOOMdb3(app_username=user.email), + user_id=user.user_id, + email=user.email, + ) + + +def _split_source(value: str) -> tuple[str, str | None]: + source = str(value or "").strip() + if not source: + raise ValueError("source EUID is required") + if "." not in source: + return source, None + euid, well = source.rsplit(".", 1) + euid = euid.strip() + well = well.strip().upper() + if not euid or not well: + raise ValueError(f"Invalid source well reference: {value!r}") + return euid, well + + +def _normalize_record(row: dict[str, Any], default_target: str | None) -> dict[str, Any]: + source = row.get("source") or row.get("source_euid") or row.get("euid") or row.get("0") + target = ( + row.get("target") + or row.get("target_container_euid") + or row.get("target_container") + or row.get("1") + or default_target + ) + target_well = row.get("target_well") or row.get("well") or row.get("2") + source_euid, source_well = _split_source(str(source or "")) + target_text = str(target or "").strip() + if not target_text: + raise ValueError(f"Target is required for source {source_euid}") + create_type = None + target_euid = target_text + if target_text.upper().startswith("CREATE."): + create_type = target_text.split(".", 1)[1].strip() + target_euid = None + if not create_type: + raise ValueError(f"CREATE target requires a type for source {source_euid}") + return { + "source_euid": source_euid, + "source_well": source_well, + "target_euid": target_euid, + "create_type": create_type, + "target_well": str(target_well or "").strip().upper() or None, + "mapping_mode": "directed" if target_well else "auto", + } + + +def _parse_rows(payload: ContainerActionScanRequest) -> list[dict[str, Any]]: + records = list(payload.records) + if payload.rows: + sample = payload.rows[:1024] + try: + dialect = csv.Sniffer().sniff(sample, delimiters=",\t") + except csv.Error: + dialect = csv.excel + reader = csv.reader(StringIO(payload.rows), dialect) + for row in reader: + if not row or not any(str(cell).strip() for cell in row): + continue + records.append({str(index): value for index, value in enumerate(row)}) + if not records: + raise ValueError("rows or records is required") + return [_normalize_record(row, payload.default_target) for row in records] + + +def _object_kind(euid: str, user: APIUser) -> dict[str, Any]: + db = BLOOMdb3(app_username=user.email) + try: + instance = db.session.execute( + select(generic_instance).where( + generic_instance.euid == euid, + generic_instance.is_deleted.is_(False), + ) + ).scalar_one_or_none() + if instance is None: + return {"euid": euid, "exists": False, "kind": None} + return { + "euid": euid, + "exists": True, + "kind": "/".join( + [ + str(getattr(instance, "category", "") or ""), + str(getattr(instance, "type", "") or ""), + str(getattr(instance, "subtype", "") or ""), + str(getattr(instance, "version", "") or ""), + ] + ), + } + finally: + db.close() + + +@router.post("/validate") +async def validate_container_action( + payload: ContainerActionScanRequest, + user: APIUser = Depends(require_write), +): + try: + planned = _parse_rows(payload) + source_types = {row["source_euid"]: _object_kind(row["source_euid"], user) for row in planned} + target_types = { + row["target_euid"]: _object_kind(row["target_euid"], user) + for row in planned + if row["target_euid"] + } + return { + "valid": True, + "row_count": len(planned), + "planned": planned, + "source_types": source_types, + "target_types": target_types, + "creates": [row for row in planned if row["create_type"]], + } + except (ValueError, ValidationError) as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc + + +@router.post("/execute") +async def execute_container_action( + payload: ContainerActionExecuteRequest, + user: APIUser = Depends(require_write), +): + validation = await validate_container_action(payload, user) + if not payload.operation_type: + raise HTTPException( + status_code=400, + detail={ + "message": ( + "operation_type is required. Container actions do not infer " + "execution behavior from scanned rows." + ), + "validation": validation, + }, + ) + if not payload.operation_payload: + raise HTTPException( + status_code=400, + detail={ + "message": "operation_payload is required for container-action execution.", + "operation_type": payload.operation_type, + "validation": validation, + }, + ) + + service = _service_for_user(user) + try: + operation_payload = payload.operation_payload + if payload.operation_type == "extraction_plate": + result = service.create_extraction_plate( + ExtractionPlateRequest.model_validate(operation_payload) + ) + elif payload.operation_type == "extraction_qc_plate": + result = service.fill_extraction_qc_plate( + ExtractionQcPlateRequest.model_validate(operation_payload) + ) + elif payload.operation_type == "plate_well_data": + result = service.attach_plate_well_data( + PlateWellDataRequest.model_validate(operation_payload) + ) + elif payload.operation_type == "seq_library_plate": + result = service.create_seq_library_plate( + SeqLibraryPlateRequest.model_validate(operation_payload) + ) + elif payload.operation_type == "seq_library_pool": + result = service.create_seq_library_pool( + SeqLibraryPoolRequest.model_validate(operation_payload) + ) + elif payload.operation_type == "seq_run_set": + result = service.create_seq_run_set( + SeqRunSetRequest.model_validate(operation_payload) + ) + elif payload.operation_type == "lab_set": + result = service.create_lab_set(LabSetRequest.model_validate(operation_payload)) + elif payload.operation_type == "print_euids": + result = service.print_euids(PrintEuidRequest.model_validate(operation_payload)) + else: # pragma: no cover - Literal validation should prevent this. + raise HTTPException(status_code=400, detail="Unsupported operation_type") + return { + "operation_type": payload.operation_type, + "validation": validation, + "result": result, + } + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc + finally: + service.close() diff --git a/bloom_lims/gui/routes/modern.py b/bloom_lims/gui/routes/modern.py index abff4b4..27a57c3 100644 --- a/bloom_lims/gui/routes/modern.py +++ b/bloom_lims/gui/routes/modern.py @@ -175,6 +175,19 @@ async def lab_actions_wizard(request: Request, _auth=Depends(require_auth)): return HTMLResponse(content=template.render(context), status_code=200) +@router.get("/container-actions", response_class=HTMLResponse) +async def container_actions_wizard(request: Request, _auth=Depends(require_auth)): + user_data = request.session.get("user_data", {}) + template = templates.get_template("modern/container_actions.html") + context = { + "request": request, + "udat": user_data, + "user": user_data, + "page_title": "Container Actions", + } + return HTMLResponse(content=template.render(context), status_code=200) + + @router.get("/help", response_class=HTMLResponse) async def help_page(request: Request): user_data = request.session.get("user_data", {}) diff --git a/templates/modern/base.html b/templates/modern/base.html index d694f78..1afaa87 100644 --- a/templates/modern/base.html +++ b/templates/modern/base.html @@ -72,6 +72,9 @@ Lab Actions + + Container Actions +